Skip to content

Commit

Permalink
portmap doesn't fail if chain doesn't exist
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
aojea committed Dec 11, 2019
1 parent 6551165 commit 8a57c17
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
30 changes: 26 additions & 4 deletions plugins/meta/portmap/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down
38 changes: 37 additions & 1 deletion plugins/meta/portmap/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"math/rand"
"runtime"
"sync"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
Expand All @@ -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() {
Expand All @@ -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))
Expand Down Expand Up @@ -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")
}
}

})
})

0 comments on commit 8a57c17

Please sign in to comment.