diff --git a/pkg/util/iptables/BUILD b/pkg/util/iptables/BUILD index 7ed0b92a48b..a8a1868cddb 100644 --- a/pkg/util/iptables/BUILD +++ b/pkg/util/iptables/BUILD @@ -13,6 +13,7 @@ go_library( srcs = [ "doc.go", "iptables.go", + "iptables_linux.go", "save_restore.go", ], tags = ["automanaged"], @@ -22,6 +23,7 @@ go_library( "//pkg/util/version:go_default_library", "//vendor/github.com/godbus/dbus:go_default_library", "//vendor/github.com/golang/glog:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", ], diff --git a/pkg/util/iptables/iptables.go b/pkg/util/iptables/iptables.go index c25cf2776f2..2d3c24dcacd 100644 --- a/pkg/util/iptables/iptables.go +++ b/pkg/util/iptables/iptables.go @@ -19,18 +19,13 @@ package iptables import ( "bytes" "fmt" - "net" - "os" "regexp" "strings" "sync" - "syscall" - "time" godbus "github.com/godbus/dbus" "github.com/golang/glog" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/wait" utildbus "k8s.io/kubernetes/pkg/util/dbus" utilexec "k8s.io/kubernetes/pkg/util/exec" utilversion "k8s.io/kubernetes/pkg/util/version" @@ -346,69 +341,8 @@ func (runner *runner) RestoreAll(data []byte, flush FlushFlag, counters RestoreC return runner.restoreInternal(args, data, flush, counters) } -type locker struct { - lock16 *os.File - lock14 *net.UnixListener -} - -func (l *locker) Close() { - if l.lock16 != nil { - l.lock16.Close() - } - if l.lock14 != nil { - l.lock14.Close() - } -} - -func (runner *runner) grabIptablesLocks() (*locker, error) { - var err error - var success bool - - l := &locker{} - defer func(l *locker) { - // Clean up immediately on failure - if !success { - l.Close() - } - }(l) - - if len(runner.restoreWaitFlag) > 0 { - // iptables-restore supports --wait; no need to grab locks - return l, nil - } - - // Grab both 1.6.x and 1.4.x-style locks; we don't know what the - // iptables-restore version is if it doesn't support --wait, so we - // can't assume which lock method it'll use. - - // Roughly duplicate iptables 1.6.x xtables_lock() function. - l.lock16, err = os.OpenFile(runner.lockfilePath, os.O_CREATE, 0600) - if err != nil { - return nil, fmt.Errorf("failed to open iptables lock %s: %v", runner.lockfilePath, err) - } - - if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) { - if err := syscall.Flock(int(l.lock16.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { - return false, nil - } - return true, nil - }); err != nil { - return nil, fmt.Errorf("failed to acquire new iptables lock: %v", err) - } - - // Roughly duplicate iptables 1.4.x xtables_lock() function. - if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) { - l.lock14, err = net.ListenUnix("unix", &net.UnixAddr{Name: "@xtables", Net: "unix"}) - if err != nil { - return false, nil - } - return true, nil - }); err != nil { - return nil, fmt.Errorf("failed to acquire old iptables lock: %v", err) - } - - success = true - return l, nil +type iptablesLocker interface { + Close() } // restoreInternal is the shared part of Restore/RestoreAll @@ -426,11 +360,13 @@ func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFla // Grab the iptables lock to prevent iptables-restore and iptables // from stepping on each other. iptables-restore 1.6.2 will have // a --wait option like iptables itself, but that's not widely deployed. - locker, err := runner.grabIptablesLocks() - if err != nil { - return err + if len(runner.restoreWaitFlag) == 0 { + locker, err := grabIptablesLocks(runner.lockfilePath) + if err != nil { + return err + } + defer locker.Close() } - defer locker.Close() // run the command and return the output or an error including the output and error fullArgs := append(runner.restoreWaitFlag, args...) diff --git a/pkg/util/iptables/iptables_linux.go b/pkg/util/iptables/iptables_linux.go new file mode 100644 index 00000000000..4f614cb523d --- /dev/null +++ b/pkg/util/iptables/iptables_linux.go @@ -0,0 +1,93 @@ +// +build linux + +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package iptables + +import ( + "fmt" + "net" + "os" + "time" + + "golang.org/x/sys/unix" + "k8s.io/apimachinery/pkg/util/wait" +) + +type locker struct { + lock16 *os.File + lock14 *net.UnixListener +} + +func (l *locker) Close() { + if l.lock16 != nil { + l.lock16.Close() + } + if l.lock14 != nil { + l.lock14.Close() + } +} + +func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) { + var err error + var success bool + + l := &locker{} + defer func(l *locker) { + // Clean up immediately on failure + if !success { + l.Close() + } + }(l) + + // Grab both 1.6.x and 1.4.x-style locks; we don't know what the + // iptables-restore version is if it doesn't support --wait, so we + // can't assume which lock method it'll use. + + // Roughly duplicate iptables 1.6.x xtables_lock() function. + l.lock16, err = os.OpenFile(lockfilePath, os.O_CREATE, 0600) + if err != nil { + return nil, fmt.Errorf("failed to open iptables lock %s: %v", lockfilePath, err) + } + + if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) { + if err := grabIptablesFileLock(l.lock16); err != nil { + return false, nil + } + return true, nil + }); err != nil { + return nil, fmt.Errorf("failed to acquire new iptables lock: %v", err) + } + + // Roughly duplicate iptables 1.4.x xtables_lock() function. + if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) { + l.lock14, err = net.ListenUnix("unix", &net.UnixAddr{Name: "@xtables", Net: "unix"}) + if err != nil { + return false, nil + } + return true, nil + }); err != nil { + return nil, fmt.Errorf("failed to acquire old iptables lock: %v", err) + } + + success = true + return l, nil +} + +func grabIptablesFileLock(f *os.File) error { + return unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB) +} diff --git a/pkg/util/iptables/iptables_test.go b/pkg/util/iptables/iptables_test.go index b38714415ec..5fb921d5220 100644 --- a/pkg/util/iptables/iptables_test.go +++ b/pkg/util/iptables/iptables_test.go @@ -20,7 +20,6 @@ import ( "net" "os" "strings" - "syscall" "testing" "time" @@ -1190,7 +1189,7 @@ func TestRestoreAllGrabNewLock(t *testing.T) { } defer runLock.Close() - if err := syscall.Flock(int(runLock.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { + if err := grabIptablesFileLock(runLock); err != nil { t.Errorf("expected to lock %s, got %v", TestLockfilePath, err) } diff --git a/pkg/util/iptables/iptables_unsupported.go b/pkg/util/iptables/iptables_unsupported.go new file mode 100644 index 00000000000..c6a5f0d7dc6 --- /dev/null +++ b/pkg/util/iptables/iptables_unsupported.go @@ -0,0 +1,32 @@ +// +build !linux + +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package iptables + +import ( + "fmt" + "os" +) + +func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) { + return nil, fmt.Errorf("iptables unsupported on this platform") +} + +func grabIptablesFileLock(f *os.File) error { + return fmt.Errorf("iptables unsupported on this platform") +}