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.