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 <antonio.ojea.garcia@gmail.com>
This commit is contained in:
parent
d8b1289098
commit
3603738c6a
@ -71,8 +71,15 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
|||||||
// This will succeed *and create the chain* if it does not exist.
|
// This will succeed *and create the chain* if it does not exist.
|
||||||
// If the chain doesn't exist, the next checks will fail.
|
// If the chain doesn't exist, the next checks will fail.
|
||||||
if err := ipt.ClearChain(c.table, c.name); err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, entryChain := range c.entryChains {
|
for _, entryChain := range c.entryChains {
|
||||||
entryChainRules, err := ipt.List(c.table, entryChain)
|
entryChainRules, err := ipt.List(c.table, entryChain)
|
||||||
@ -91,16 +98,31 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
|||||||
chainParts = chainParts[2:] // List results always include an -A CHAINNAME
|
chainParts = chainParts[2:] // List results always include an -A CHAINNAME
|
||||||
|
|
||||||
if err := ipt.Delete(c.table, entryChain, chainParts...); err != nil {
|
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)
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertUnique will add a rule to a chain if it does not already exist.
|
// insertUnique will add a rule to a chain if it does not already exist.
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
@ -32,6 +33,7 @@ const TABLE = "filter" // We'll monkey around here
|
|||||||
var _ = Describe("chain tests", func() {
|
var _ = Describe("chain tests", func() {
|
||||||
var testChain chain
|
var testChain chain
|
||||||
var ipt *iptables.IPTables
|
var ipt *iptables.IPTables
|
||||||
|
var testNs ns.NetNS
|
||||||
var cleanup func()
|
var cleanup func()
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
@ -41,7 +43,7 @@ var _ = Describe("chain tests", func() {
|
|||||||
currNs, err := ns.GetCurrentNS()
|
currNs, err := ns.GetCurrentNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
testNs, err := testutils.NewNS()
|
testNs, err = testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
tlChainName := fmt.Sprintf("cni-test-%d", rand.Intn(10000000))
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user