From d8b1289098589edb0d451b02a044be43cd2a17af Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Wed, 11 Dec 2019 19:00:26 +0100 Subject: [PATCH 1/4] fix portmap port forward flakiness Use a Describe container for the It code block of the portmap port forward integration test. Signed-off-by: Antonio Ojea --- plugins/meta/portmap/portmap_integ_test.go | 208 +++++++++++---------- 1 file changed, 111 insertions(+), 97 deletions(-) diff --git a/plugins/meta/portmap/portmap_integ_test.go b/plugins/meta/portmap/portmap_integ_test.go index ce4eebef..22a7f5bd 100644 --- a/plugins/meta/portmap/portmap_integ_test.go +++ b/plugins/meta/portmap/portmap_integ_test.go @@ -96,119 +96,133 @@ var _ = Describe("portmap integration tests", func() { } }) - // This needs to be done using Ginkgo's asynchronous testing mode. - It("forwards a TCP port on ipv4", func(done Done) { - var err error - hostPort := rand.Intn(10000) + 1025 - runtimeConfig := libcni.RuntimeConf{ - ContainerID: fmt.Sprintf("unit-test-%d", hostPort), - NetNS: targetNS.Path(), - IfName: "eth0", - CapabilityArgs: map[string]interface{}{ - "portMappings": []map[string]interface{}{ - { - "hostPort": hostPort, - "containerPort": containerPort, - "protocol": "tcp", + Describe("Creating an interface in a namespace with the ptp plugin", func() { + // This needs to be done using Ginkgo's asynchronous testing mode. + It("forwards a TCP port on ipv4", func(done Done) { + var err error + hostPort := rand.Intn(10000) + 1025 + runtimeConfig := libcni.RuntimeConf{ + ContainerID: fmt.Sprintf("unit-test-%d", hostPort), + NetNS: targetNS.Path(), + IfName: "eth0", + CapabilityArgs: map[string]interface{}{ + "portMappings": []map[string]interface{}{ + { + "hostPort": hostPort, + "containerPort": containerPort, + "protocol": "tcp", + }, }, }, - }, - } - - // Make delete idempotent, so we can clean up on failure - netDeleted := false - deleteNetwork := func() error { - if netDeleted { - return nil } - netDeleted = true - return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) - } - // we'll also manually check the iptables chains - ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) - Expect(err).NotTo(HaveOccurred()) - dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name - - // Create the network - resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) - Expect(err).NotTo(HaveOccurred()) - defer deleteNetwork() - - // Undo Docker's forwarding policy - cmd := exec.Command("iptables", "-t", "filter", - "-P", "FORWARD", "ACCEPT") - cmd.Stderr = GinkgoWriter - err = cmd.Run() - Expect(err).NotTo(HaveOccurred()) - - // Check the chain exists - _, err = ipt.List("nat", dnatChainName) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(resI) - Expect(err).NotTo(HaveOccurred()) - var contIP net.IP - - for _, ip := range result.IPs { - intfIndex := *ip.Interface - if result.Interfaces[intfIndex].Sandbox == "" { - continue + // Make delete idempotent, so we can clean up on failure + netDeleted := false + deleteNetwork := func() error { + if netDeleted { + return nil + } + netDeleted = true + return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) } - contIP = ip.Address.IP - } - if contIP == nil { - Fail("could not determine container IP") - } - hostIP := getLocalIP() - fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", - hostIP, hostPort, contIP, containerPort) + // we'll also manually check the iptables chains + ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) + Expect(err).NotTo(HaveOccurred()) + dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name - // dump iptables-save output for debugging - cmd = exec.Command("iptables-save") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + // Create the network + resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) + Expect(err).NotTo(HaveOccurred()) + defer deleteNetwork() - // Sanity check: verify that the container is reachable directly - contOK := testEchoServer(contIP.String(), containerPort, "") + // Undo Docker's forwarding policy + cmd := exec.Command("iptables", "-t", "filter", + "-P", "FORWARD", "ACCEPT") + cmd.Stderr = GinkgoWriter + err = cmd.Run() + Expect(err).NotTo(HaveOccurred()) - // Verify that a connection to the forwarded port works - dnatOK := testEchoServer(hostIP, hostPort, "") + // Check the chain exists + _, err = ipt.List("nat", dnatChainName) + Expect(err).NotTo(HaveOccurred()) - // Verify that a connection to localhost works - snatOK := testEchoServer("127.0.0.1", hostPort, "") + result, err := current.GetResult(resI) + Expect(err).NotTo(HaveOccurred()) + var contIP net.IP - // verify that hairpin works - hairpinOK := testEchoServer(hostIP, hostPort, targetNS.Path()) + for _, ip := range result.IPs { + intfIndex := *ip.Interface + if result.Interfaces[intfIndex].Sandbox == "" { + continue + } + contIP = ip.Address.IP + } + if contIP == nil { + Fail("could not determine container IP") + } - // Cleanup - session.Terminate() - err = deleteNetwork() - Expect(err).NotTo(HaveOccurred()) + hostIP := getLocalIP() + fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", + hostIP, hostPort, contIP, containerPort) - // Verify iptables rules are gone - _, err = ipt.List("nat", dnatChainName) - Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) + // dump iptables-save output for debugging + cmd = exec.Command("iptables-save") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Check that everything succeeded *after* we clean up the network - if !contOK { - Fail("connection direct to " + contIP.String() + " failed") - } - if !dnatOK { - Fail("Connection to " + hostIP + " was not forwarded") - } - if !snatOK { - Fail("connection to 127.0.0.1 was not forwarded") - } - if !hairpinOK { - Fail("Hairpin connection failed") - } + // dump ip routes output for debugging + cmd = exec.Command("ip", "route") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - close(done) + // dump ip addresses output for debugging + cmd = exec.Command("ip", "addr") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - }, TIMEOUT*9) + // Sanity check: verify that the container is reachable directly + contOK := testEchoServer(contIP.String(), containerPort, "") + + // Verify that a connection to the forwarded port works + dnatOK := testEchoServer(hostIP, hostPort, "") + + // Verify that a connection to localhost works + snatOK := testEchoServer("127.0.0.1", hostPort, "") + + // verify that hairpin works + hairpinOK := testEchoServer(hostIP, hostPort, targetNS.Path()) + + // Cleanup + session.Terminate() + err = deleteNetwork() + Expect(err).NotTo(HaveOccurred()) + + // Verify iptables rules are gone + _, err = ipt.List("nat", dnatChainName) + Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) + + // Check that everything succeeded *after* we clean up the network + if !contOK { + Fail("connection direct to " + contIP.String() + " failed") + } + if !dnatOK { + Fail("Connection to " + hostIP + " was not forwarded") + } + if !snatOK { + Fail("connection to 127.0.0.1 was not forwarded") + } + if !hairpinOK { + Fail("Hairpin connection failed") + } + + close(done) + + }, TIMEOUT*9) + }) }) // testEchoServer returns true if we found an echo server on the port From 3603738c6a744d2d68f26f097d296ab7d9997cf8 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Mon, 9 Dec 2019 15:35:32 +0100 Subject: [PATCH 2/4] portmap doesn't fail if chain doesn't exist It turns out that the portmap plugin is not idempotent if its executed in parallel. The errors are caused due to a race of different instantiations deleting the chains. This patch does that the portmap plugin doesn't fail if the errors are because the chain doesn't exist on teardown. Signed-off-by: Antonio Ojea --- plugins/meta/portmap/chain.go | 30 +++++++++++++++++++---- plugins/meta/portmap/chain_test.go | 38 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/plugins/meta/portmap/chain.go b/plugins/meta/portmap/chain.go index 8e8dbe3f..533a5193 100644 --- a/plugins/meta/portmap/chain.go +++ b/plugins/meta/portmap/chain.go @@ -71,7 +71,14 @@ func (c *chain) teardown(ipt *iptables.IPTables) error { // This will succeed *and create the chain* if it does not exist. // If the chain doesn't exist, the next checks will fail. if err := ipt.ClearChain(c.table, c.name); err != nil { - return err + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + return nil + default: + return err + } } for _, entryChain := range c.entryChains { @@ -91,16 +98,31 @@ func (c *chain) teardown(ipt *iptables.IPTables) error { chainParts = chainParts[2:] // List results always include an -A CHAINNAME if err := ipt.Delete(c.table, entryChain, chainParts...); err != nil { - return fmt.Errorf("Failed to delete referring rule %s %s: %v", c.table, entryChainRule, err) + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + continue + case eok && eerr.ExitStatus() == 2: + // swallow here, invalid command line parameter because the referring rule is missing + continue + default: + return fmt.Errorf("Failed to delete referring rule %s %s: %v", c.table, entryChainRule, err) + } } } } } - if err := ipt.DeleteChain(c.table, c.name); err != nil { + err := ipt.DeleteChain(c.table, c.name) + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + return nil + default: return err } - return nil } // insertUnique will add a rule to a chain if it does not already exist. diff --git a/plugins/meta/portmap/chain_test.go b/plugins/meta/portmap/chain_test.go index 93e4be13..c4e41be5 100644 --- a/plugins/meta/portmap/chain_test.go +++ b/plugins/meta/portmap/chain_test.go @@ -18,6 +18,7 @@ import ( "fmt" "math/rand" "runtime" + "sync" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -32,6 +33,7 @@ const TABLE = "filter" // We'll monkey around here var _ = Describe("chain tests", func() { var testChain chain var ipt *iptables.IPTables + var testNs ns.NetNS var cleanup func() BeforeEach(func() { @@ -41,7 +43,7 @@ var _ = Describe("chain tests", func() { currNs, err := ns.GetCurrentNS() Expect(err).NotTo(HaveOccurred()) - testNs, err := testutils.NewNS() + testNs, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) tlChainName := fmt.Sprintf("cni-test-%d", rand.Intn(10000000)) @@ -195,4 +197,38 @@ var _ = Describe("chain tests", func() { } } }) + + It("deletes chains idempotently in parallel", func() { + defer cleanup() + // number of parallel executions + N := 10 + var wg sync.WaitGroup + err := testChain.setup(ipt) + Expect(err).NotTo(HaveOccurred()) + errCh := make(chan error, N) + for i := 0; i < N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // teardown chain + errCh <- testNs.Do(func(ns.NetNS) error { + return testChain.teardown(ipt) + }) + }() + } + wg.Wait() + close(errCh) + for err := range errCh { + Expect(err).NotTo(HaveOccurred()) + } + + chains, err := ipt.ListChains(TABLE) + Expect(err).NotTo(HaveOccurred()) + for _, chain := range chains { + if chain == testChain.name { + Fail("Chain was not deleted") + } + } + + }) }) From bf8f171041bfce29f75528be75680d7033578257 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Thu, 12 Dec 2019 14:03:33 +0100 Subject: [PATCH 3/4] iptables: add idempotent functions Add the following idempotent functions to iptables utils: DeleteRule: idempotently delete an iptables rule DeleteChain: idempotently delete an iptables chain ClearChain: idempotently flush an iptables chain Signed-off-by: Antonio Ojea --- pkg/utils/iptables.go | 57 +++++++++++++++++++++++++++++++++++ pkg/utils/iptables_test.go | 31 +++++++++++++++---- plugins/meta/portmap/chain.go | 36 ++++------------------ 3 files changed, 88 insertions(+), 36 deletions(-) diff --git a/pkg/utils/iptables.go b/pkg/utils/iptables.go index f1a61696..b38a2cd0 100644 --- a/pkg/utils/iptables.go +++ b/pkg/utils/iptables.go @@ -62,3 +62,60 @@ func ChainExists(ipt *iptables.IPTables, table, chain string) (bool, error) { } return false, nil } + +// DeleteRule idempotently delete the iptables rule in the specified table/chain. +// It does not return an error if the referring chain doesn't exist +func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error { + if ipt == nil { + return errors.New("failed to ensure iptable chain: IPTables was nil") + } + if err := ipt.Delete(table, chain, rulespec...); err != nil { + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + return nil + case eok && eerr.ExitStatus() == 2: + // swallow here, invalid command line parameter because the referring rule is missing + return nil + default: + return fmt.Errorf("Failed to delete referring rule %s %s: %v", table, chain, err) + } + } + return nil +} + +// DeleteChain idempotently deletes the specified table/chain. +// It does not return an errors if the chain does not exist +func DeleteChain(ipt *iptables.IPTables, table, chain string) error { + if ipt == nil { + return errors.New("failed to ensure iptable chain: IPTables was nil") + } + + err := ipt.DeleteChain(table, chain) + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + return nil + default: + return err + } +} + +// ClearChain idempotently clear the iptables rules in the specified table/chain. +// If the chain does not exist, a new one will be created +func ClearChain(ipt *iptables.IPTables, table, chain string) error { + if ipt == nil { + return errors.New("failed to ensure iptable chain: IPTables was nil") + } + err := ipt.ClearChain(table, chain) + eerr, eok := err.(*iptables.Error) + switch { + case eok && eerr.IsNotExist(): + // swallow here, the chain was already deleted + return EnsureChain(ipt, table, chain) + default: + return err + } +} diff --git a/pkg/utils/iptables_test.go b/pkg/utils/iptables_test.go index e293f22a..dee73d63 100644 --- a/pkg/utils/iptables_test.go +++ b/pkg/utils/iptables_test.go @@ -67,12 +67,31 @@ var _ = Describe("chain tests", func() { cleanup() }) - It("creates chains idempotently", func() { - err := EnsureChain(ipt, TABLE, testChain) - Expect(err).NotTo(HaveOccurred()) + Describe("EnsureChain", func() { + It("creates chains idempotently", func() { + err := EnsureChain(ipt, TABLE, testChain) + Expect(err).NotTo(HaveOccurred()) - // Create it again! - err = EnsureChain(ipt, TABLE, testChain) - Expect(err).NotTo(HaveOccurred()) + // Create it again! + err = EnsureChain(ipt, TABLE, testChain) + Expect(err).NotTo(HaveOccurred()) + }) }) + + Describe("DeleteChain", func() { + It("delete chains idempotently", func() { + // Create chain + err := EnsureChain(ipt, TABLE, testChain) + Expect(err).NotTo(HaveOccurred()) + + // Delete chain + err = DeleteChain(ipt, TABLE, testChain) + Expect(err).NotTo(HaveOccurred()) + + // Delete it again! + err = DeleteChain(ipt, TABLE, testChain) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) diff --git a/plugins/meta/portmap/chain.go b/plugins/meta/portmap/chain.go index 533a5193..4875b5e2 100644 --- a/plugins/meta/portmap/chain.go +++ b/plugins/meta/portmap/chain.go @@ -70,15 +70,8 @@ func (c *chain) teardown(ipt *iptables.IPTables) error { // flush the chain // This will succeed *and create the chain* if it does not exist. // If the chain doesn't exist, the next checks will fail. - if err := ipt.ClearChain(c.table, c.name); err != nil { - eerr, eok := err.(*iptables.Error) - switch { - case eok && eerr.IsNotExist(): - // swallow here, the chain was already deleted - return nil - default: - return err - } + if err := utils.ClearChain(ipt, c.table, c.name); err != nil { + return err } for _, entryChain := range c.entryChains { @@ -97,32 +90,15 @@ func (c *chain) teardown(ipt *iptables.IPTables) error { } chainParts = chainParts[2:] // List results always include an -A CHAINNAME - if err := ipt.Delete(c.table, entryChain, chainParts...); err != nil { - eerr, eok := err.(*iptables.Error) - switch { - case eok && eerr.IsNotExist(): - // swallow here, the chain was already deleted - continue - case eok && eerr.ExitStatus() == 2: - // swallow here, invalid command line parameter because the referring rule is missing - continue - default: - return fmt.Errorf("Failed to delete referring rule %s %s: %v", c.table, entryChainRule, err) - } + if err := utils.DeleteRule(ipt, c.table, entryChain, chainParts...); err != nil { + return err } + } } } - err := ipt.DeleteChain(c.table, c.name) - eerr, eok := err.(*iptables.Error) - switch { - case eok && eerr.IsNotExist(): - // swallow here, the chain was already deleted - return nil - default: - return err - } + return utils.DeleteChain(ipt, c.table, c.name) } // insertUnique will add a rule to a chain if it does not already exist. From 5a02c5bc61f182cfa384bbcbcf62230c35894115 Mon Sep 17 00:00:00 2001 From: Antonio Ojea Date: Fri, 13 Dec 2019 12:58:23 +0100 Subject: [PATCH 4/4] bump go-iptables module to v0.4.5 bump the go-iptables module to v0.4.5 to avoid concurrency issues with the portmap plugin and errors related to iptables not able to hold the lock. Signed-off-by: Antonio Ojea --- go.mod | 2 +- go.sum | 4 +++ .../coreos/go-iptables/iptables/iptables.go | 29 +++++++++---------- vendor/modules.txt | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index b05899593..52156ff0 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 github.com/containernetworking/cni v0.7.1 - github.com/coreos/go-iptables v0.4.2 + github.com/coreos/go-iptables v0.4.5 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c github.com/d2g/dhcp4client v1.0.0 diff --git a/go.sum b/go.sum index 024acd02..d72ae0ed 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,10 @@ github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg= github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.4.4 h1:5oOUvU7Fk53Hn/rkdJ0zcYGCffotqXpyi4ADCkO1TJ8= +github.com/coreos/go-iptables v0.4.4/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc= diff --git a/vendor/github.com/coreos/go-iptables/iptables/iptables.go b/vendor/github.com/coreos/go-iptables/iptables/iptables.go index 2ed875bb..1074275b 100644 --- a/vendor/github.com/coreos/go-iptables/iptables/iptables.go +++ b/vendor/github.com/coreos/go-iptables/iptables/iptables.go @@ -48,9 +48,13 @@ func (e *Error) Error() string { // IsNotExist returns true if the error is due to the chain or rule not existing func (e *Error) IsNotExist() bool { - return e.ExitStatus() == 1 && - (e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) || - e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto))) + if e.ExitStatus() != 1 { + return false + } + cmdIptables := getIptablesCommand(e.proto) + msgNoRuleExist := fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", cmdIptables) + msgNoChainExist := fmt.Sprintf("%s: No chain/target/match by that name.\n", cmdIptables) + return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) } // Protocol to differentiate between IPv4 and IPv6 @@ -101,7 +105,13 @@ func NewWithProtocol(proto Protocol) (*IPTables, error) { return nil, err } vstring, err := getIptablesVersionString(path) + if err != nil { + return nil, fmt.Errorf("could not get iptables version: %v", err) + } v1, v2, v3, mode, err := extractIptablesVersion(vstring) + if err != nil { + return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) + } checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) @@ -348,18 +358,6 @@ func (ipt *IPTables) executeList(args []string) ([]string, error) { rules = rules[:len(rules)-1] } - // nftables mode doesn't return an error code when listing a non-existent - // chain. Patch that up. - if len(rules) == 0 && ipt.mode == "nf_tables" { - v := 1 - return nil, &Error{ - cmd: exec.Cmd{Args: args}, - msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)), - proto: ipt.proto, - exitStatus: &v, - } - } - for i, rule := range rules { rules[i] = filterRuleOutput(rule) } @@ -437,6 +435,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { } ul, err := fmu.tryLock() if err != nil { + syscall.Close(fmu.fd) return err } defer ul.Unlock() diff --git a/vendor/modules.txt b/vendor/modules.txt index 6bcd9da3..18642ac9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -32,7 +32,7 @@ github.com/containernetworking/cni/pkg/skel github.com/containernetworking/cni/pkg/version github.com/containernetworking/cni/pkg/types/020 github.com/containernetworking/cni/libcni -# github.com/coreos/go-iptables v0.4.2 +# github.com/coreos/go-iptables v0.4.5 github.com/coreos/go-iptables/iptables # github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/coreos/go-systemd/activation