Do our best not to invoke iptables concurrently if --wait is unsupported
We encountered a situation where concurrent invocations of the docker daemon on a machine with an older version of iptables led to nondeterministic errors related to simultaenous invocations of iptables. While this is best resolved by upgrading iptables itself, the particular situation would have been avoided if the docker daemon simply took care not to concurrently invoke iptables. Of course, external processes could also cause iptables to fail in this way, but invoking docker in parallel seems like a pretty common case. Signed-off-by: Aaron Davidson <aaron@databricks.com>
This commit is contained in:
parent
861cba008d
commit
81e1cdca21
2 changed files with 48 additions and 2 deletions
|
@ -8,6 +8,7 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
@ -27,6 +28,8 @@ const (
|
|||
var (
|
||||
iptablesPath string
|
||||
supportsXlock = false
|
||||
// used to lock iptables commands if xtables lock is not supported
|
||||
bestEffortLock sync.Mutex
|
||||
ErrIptablesNotFound = errors.New("Iptables not found")
|
||||
)
|
||||
|
||||
|
@ -288,6 +291,9 @@ func Raw(args ...string) ([]byte, error) {
|
|||
}
|
||||
if supportsXlock {
|
||||
args = append([]string{"--wait"}, args...)
|
||||
} else {
|
||||
bestEffortLock.Lock()
|
||||
defer bestEffortLock.Unlock()
|
||||
}
|
||||
|
||||
logrus.Debugf("%s, %v", iptablesPath, args)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -169,6 +170,45 @@ func TestOutput(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConcurrencyWithWait(t *testing.T) {
|
||||
RunConcurrencyTest(t, true)
|
||||
}
|
||||
|
||||
func TestConcurrencyNoWait(t *testing.T) {
|
||||
RunConcurrencyTest(t, false)
|
||||
}
|
||||
|
||||
// Runs 10 concurrent rule additions. This will fail if iptables
|
||||
// is actually invoked simultaneously without --wait.
|
||||
// Note that if iptables does not support the xtable lock on this
|
||||
// system, then allowXlock has no effect -- it will always be off.
|
||||
func RunConcurrencyTest(t *testing.T, allowXlock bool) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
if !allowXlock && supportsXlock {
|
||||
supportsXlock = false
|
||||
defer func() { supportsXlock = true }()
|
||||
}
|
||||
|
||||
ip := net.ParseIP("192.168.1.1")
|
||||
port := 1234
|
||||
dstAddr := "172.17.0.1"
|
||||
dstPort := 4321
|
||||
proto := "tcp"
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := natChain.Forward(Append, ip, port, proto, dstAddr, dstPort)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCleanup(t *testing.T) {
|
||||
var err error
|
||||
var rules []byte
|
||||
|
|
Loading…
Reference in a new issue