diff --git a/deploy/handler/operator.yaml b/deploy/handler/operator.yaml index f79ab92ac..40713877a 100644 --- a/deploy/handler/operator.yaml +++ b/deploy/handler/operator.yaml @@ -219,8 +219,8 @@ spec: readinessProbe: exec: command: - - nmstatectl - - show + - cat + - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 1 diff --git a/go.mod b/go.mod index f45cc909a..3ad02296f 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,9 @@ require ( github.com/github-release/github-release v0.10.0 github.com/go-logr/logr v0.3.0 github.com/gobwas/glob v0.2.3 + github.com/gofrs/flock v0.8.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/nightlyone/lockfile v1.0.0 + github.com/nightlyone/lockfile v1.0.0 // indirect github.com/onsi/ginkgo v1.15.0 github.com/onsi/gomega v1.10.5 github.com/openshift/cluster-network-operator v0.0.0-20200922032245-f47200e8dbc0 diff --git a/go.sum b/go.sum index 425022142..80bd823c2 100644 --- a/go.sum +++ b/go.sum @@ -667,6 +667,7 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg= github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= diff --git a/main.go b/main.go index af77f83c8..4f5f61216 100644 --- a/main.go +++ b/main.go @@ -32,8 +32,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" // +kubebuilder:scaffold:imports + "github.com/gofrs/flock" "github.com/kelseyhightower/envconfig" - "github.com/nightlyone/lockfile" "github.com/pkg/errors" "github.com/qinqon/kube-admission-webhook/pkg/certificate" "k8s.io/apimachinery/pkg/util/wait" @@ -42,6 +42,8 @@ import ( nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1" "github.com/nmstate/kubernetes-nmstate/controllers" "github.com/nmstate/kubernetes-nmstate/pkg/environment" + "github.com/nmstate/kubernetes-nmstate/pkg/file" + "github.com/nmstate/kubernetes-nmstate/pkg/nmstatectl" "github.com/nmstate/kubernetes-nmstate/pkg/webhook" ) @@ -186,6 +188,25 @@ func main() { setupLog.Error(err, "unable to create NodeNetworkState controller", "controller", "NMState") os.Exit(1) } + + // Check that nmstatectl is working + _, err := nmstatectl.Show() + if err != nil { + os.Exit(1) + setupLog.Error(err, "failed checking nmstatectl health") + } + + // Handler runs with host networking so opening ports is problematic + // they will collide with node ports so to ensure that we reach this + // point (we have the handler lock and nmstatectl show is working) a + // file is touched and the file is checked at readinessProbe field. + healthyFile := "/tmp/healthy" + setupLog.Info("Marking handler as healthy touching healthy file", "healthyFile", healthyFile) + err = file.Touch(healthyFile) + if err != nil { + os.Exit(1) + setupLog.Error(err, "failed marking handler as healthy") + } } setProfiler() @@ -214,23 +235,20 @@ func setProfiler() { } } -func lockHandler() (lockfile.Lockfile, error) { +func lockHandler() (*flock.Flock, error) { lockFilePath, ok := os.LookupEnv("NMSTATE_INSTANCE_NODE_LOCK_FILE") if !ok { - return "", errors.New("Failed to find NMSTATE_INSTANCE_NODE_LOCK_FILE ENV var") + return nil, errors.New("Failed to find NMSTATE_INSTANCE_NODE_LOCK_FILE ENV var") } setupLog.Info(fmt.Sprintf("Try to take exclusive lock on file: %s", lockFilePath)) - handlerLock, err := lockfile.New(lockFilePath) - if err != nil { - return handlerLock, errors.Wrapf(err, "failed to create lockFile for %s", lockFilePath) - } - err = wait.PollImmediateInfinite(5*time.Second, func() (done bool, err error) { - err = handlerLock.TryLock() + handlerLock := flock.New(lockFilePath) + err := wait.PollImmediateInfinite(5*time.Second, func() (done bool, err error) { + locked, err := handlerLock.TryLock() if err != nil { setupLog.Error(err, "retrying to lock handler") return false, nil // Don't return the error here, it will not re-poll if we do } - return true, nil + return locked, nil }) return handlerLock, err } diff --git a/pkg/file/touch.go b/pkg/file/touch.go new file mode 100644 index 000000000..485df2a11 --- /dev/null +++ b/pkg/file/touch.go @@ -0,0 +1,24 @@ +package file + +import ( + "os" + "time" +) + +func Touch(fileName string) error { + _, err := os.Stat(fileName) + if os.IsNotExist(err) { + file, err := os.Create(fileName) + if err != nil { + return err + } + defer file.Close() + } else { + currentTime := time.Now().Local() + err = os.Chtimes(fileName, currentTime, currentTime) + if err != nil { + return err + } + } + return nil +} diff --git a/test/e2e/daemonset/daemonset.go b/test/e2e/daemonset/daemonset.go index 2720e6b40..2508bc120 100644 --- a/test/e2e/daemonset/daemonset.go +++ b/test/e2e/daemonset/daemonset.go @@ -21,6 +21,14 @@ func GetEventually(daemonSetKey types.NamespacedName) AsyncAssertion { }, 180*time.Second, 1*time.Second) } +func GetConsistently(daemonSetKey types.NamespacedName) AsyncAssertion { + return Consistently(func() (appsv1.DaemonSet, error) { + daemonSet := appsv1.DaemonSet{} + err := testenv.Client.Get(context.TODO(), daemonSetKey, &daemonSet) + return daemonSet, err + }, 15*time.Second, 1*time.Second) +} + // GetDaemonSetList returns a DaemonSetList matching the labels passed func GetList(filteringLabels map[string]string) (appsv1.DaemonSetList, error) { ds := appsv1.DaemonSetList{} diff --git a/test/e2e/operator/main_test.go b/test/e2e/operator/main_test.go index 40bdeb336..b8c5f35aa 100644 --- a/test/e2e/operator/main_test.go +++ b/test/e2e/operator/main_test.go @@ -2,6 +2,8 @@ package operator import ( "context" + "fmt" + "os" "testing" "time" @@ -11,44 +13,39 @@ import ( ginkgoreporters "kubevirt.io/qe-tools/pkg/ginkgo-reporters" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" dynclient "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1" testenv "github.com/nmstate/kubernetes-nmstate/test/env" knmstatereporter "github.com/nmstate/kubernetes-nmstate/test/reporter" ) var ( - t *testing.T - nodes []string - startTime time.Time + t *testing.T + nodes []string + startTime time.Time + defaultNMState = nmstatev1beta1.NMState{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nmstate", + Namespace: "nmstate", + }, + } + webhookKey = types.NamespacedName{Namespace: "nmstate", Name: "nmstate-webhook"} + handlerKey = types.NamespacedName{Namespace: "nmstate", Name: "nmstate-handler"} + handlerLabels = map[string]string{"component": "kubernetes-nmstate-handler"} ) -var _ = BeforeSuite(func() { - - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - testenv.Start() - -}) - -func TestMain(m *testing.M) { +func TestE2E(t *testing.T) { testenv.TestMain() -} -func TestE2E(tapi *testing.T) { - t = tapi RegisterFailHandler(Fail) - By("Getting node list from cluster") - nodeList := corev1.NodeList{} - err := testenv.Client.List(context.TODO(), &nodeList, &dynclient.ListOptions{}) - Expect(err).ToNot(HaveOccurred()) - for _, node := range nodeList.Items { - nodes = append(nodes, node.Name) - } - reporters := make([]Reporter, 0) reporters = append(reporters, knmstatereporter.New("test_logs/e2e/operator", testenv.OperatorNamespace, nodes)) if ginkgoreporters.Polarion.Run { @@ -61,8 +58,38 @@ func TestE2E(tapi *testing.T) { RunSpecsWithDefaultAndCustomReporters(t, "Operator E2E Test Suite", reporters) } -var _ = BeforeEach(func() { +var _ = BeforeSuite(func() { + + // Change to root directory some test expect that + os.Chdir("../../../") + + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + testenv.Start() + + By("Getting node list from cluster") + nodeList := corev1.NodeList{} + err := testenv.Client.List(context.TODO(), &nodeList, &dynclient.ListOptions{}) + Expect(err).ToNot(HaveOccurred()) + for _, node := range nodeList.Items { + nodes = append(nodes, node.Name) + } }) -var _ = AfterEach(func() { +var _ = AfterSuite(func() { + uninstallNMState(defaultNMState) }) + +func installNMState(nmstate nmstatev1beta1.NMState) { + By(fmt.Sprintf("Creating NMState CR '%s'", nmstate.Name)) + err := testenv.Client.Create(context.TODO(), &nmstate) + ExpectWithOffset(1, err).ToNot(HaveOccurred(), "NMState CR created without error") +} + +func uninstallNMState(nmstate nmstatev1beta1.NMState) { + By(fmt.Sprintf("Deleting NMState CR '%s'", nmstate.Name)) + err := testenv.Client.Delete(context.TODO(), &nmstate, &client.DeleteOptions{}) + if !apierrors.IsNotFound(err) { + Expect(err).ToNot(HaveOccurred(), "NMState CR successfully removed") + } +} diff --git a/test/e2e/operator/nmstate_install_test.go b/test/e2e/operator/nmstate_install_test.go index 0d3b0ea86..99e3c1239 100644 --- a/test/e2e/operator/nmstate_install_test.go +++ b/test/e2e/operator/nmstate_install_test.go @@ -7,46 +7,35 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1" "github.com/nmstate/kubernetes-nmstate/test/e2e/daemonset" "github.com/nmstate/kubernetes-nmstate/test/e2e/deployment" + "github.com/nmstate/kubernetes-nmstate/test/cmd" testenv "github.com/nmstate/kubernetes-nmstate/test/env" ) -var ( - defaultNMState = nmstatev1beta1.NMState{ - ObjectMeta: metav1.ObjectMeta{ - Name: "nmstate", - Namespace: "nmstate", - }, - } - webhookKey = types.NamespacedName{Namespace: "nmstate", Name: "nmstate-webhook"} - handlerKey = types.NamespacedName{Namespace: "nmstate", Name: "nmstate-handler"} - handlerLabels = map[string]string{"component": "kubernetes-nmstate-handler"} -) - var _ = Describe("NMState operator", func() { Context("when installed for the first time", func() { BeforeEach(func() { - installDefaultNMState() + installNMState(defaultNMState) }) It("should deploy daemonset and webhook deployment", func() { daemonset.GetEventually(handlerKey).Should(daemonset.BeReady()) deployment.GetEventually(webhookKey).Should(deployment.BeReady()) }) AfterEach(func() { - uninstallDefaultNMState() + uninstallNMState(defaultNMState) }) }) Context("when NMState is installed", func() { It("should list one NMState CR", func() { - installDefaultNMState() + installNMState(defaultNMState) daemonset.GetEventually(handlerKey).Should(daemonset.BeReady()) ds, err := daemonset.GetList(handlerLabels) Expect(err).ToNot(HaveOccurred(), "List daemon sets in namespace nmstate succeeds") @@ -71,7 +60,7 @@ var _ = Describe("NMState operator", func() { }) Context("and uninstalled", func() { BeforeEach(func() { - uninstallDefaultNMState() + uninstallNMState(defaultNMState) }) It("should uninstall handler and webhook", func() { Eventually(func() bool { @@ -85,24 +74,55 @@ var _ = Describe("NMState operator", func() { }) }) }) + Context("when another handler is installed with different namespace", func() { + var ( + operatorNamespace = "nmstate-alt" + ) + BeforeEach(func() { + installNMState(defaultNMState) + daemonset.GetEventually(handlerKey).Should(daemonset.BeReady()) + installOperator(operatorNamespace) + }) + AfterEach(func() { + uninstallNMState(defaultNMState) + uninstallOperator(operatorNamespace) + installOperator("nmstate") + }) + It("should wait on the old one to be deleted", func() { + By("Checking handler is locked") + daemonset.GetConsistently(types.NamespacedName{Namespace: operatorNamespace, Name: "nmstate-handler"}).ShouldNot(daemonset.BeReady()) + uninstallOperator("nmstate") + By("Checking handler is unlocked after deleting old one") + daemonset.GetEventually(types.NamespacedName{Namespace: operatorNamespace, Name: "nmstate-handler"}).Should(daemonset.BeReady()) + }) + }) }) -func installNMState(nmstate nmstatev1beta1.NMState) { - err := testenv.Client.Create(context.TODO(), &nmstate) - Expect(err).ToNot(HaveOccurred(), "NMState CR created without error") -} - -func installDefaultNMState() { - installNMState(defaultNMState) -} +func installOperator(namespace string) error { + By(fmt.Sprintf("Creating NMState operator with namespace '%s'", namespace)) + _, err := cmd.Run("make", false, fmt.Sprintf("OPERATOR_NAMESPACE=%s", namespace), fmt.Sprintf("HANDLER_NAMESPACE=%s", namespace), "IMAGE_REGISTRY=registry:5000", "manifests") + Expect(err).ToNot(HaveOccurred()) -func uninstallNMState(nmstate nmstatev1beta1.NMState) { - err := testenv.Client.Delete(context.TODO(), &nmstate, &client.DeleteOptions{}) - if !apierrors.IsNotFound(err) { - Expect(err).ToNot(HaveOccurred(), "NMState CR successfully removed") + manifestsDir := "build/_output/manifests/" + manifests := []string{"namespace.yaml", "service_account.yaml", "operator.yaml", "role.yaml", "role_binding.yaml"} + for _, manifest := range manifests { + _, err = cmd.Kubectl("apply", "-f", manifestsDir+manifest) + Expect(err).ToNot(HaveOccurred()) } + deployment.GetEventually(types.NamespacedName{Namespace: namespace, Name: "nmstate-operator"}).Should(deployment.BeReady()) + + return nil } -func uninstallDefaultNMState() { - uninstallNMState(defaultNMState) +func uninstallOperator(namespace string) { + By(fmt.Sprintf("Deleting namespace '%s'", namespace)) + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + }, + } + Expect(testenv.Client.Delete(context.TODO(), &ns)).To(SatisfyAny(Succeed(), WithTransform(apierrors.IsNotFound, BeTrue()))) + Eventually(func() error { + return testenv.Client.Get(context.TODO(), types.NamespacedName{Name: namespace}, &ns) + }, 2*time.Minute, 5*time.Second).Should(SatisfyAll(HaveOccurred(), WithTransform(apierrors.IsNotFound, BeTrue()))) } diff --git a/vendor/github.com/nightlyone/lockfile/.gitignore b/vendor/github.com/gofrs/flock/.gitignore similarity index 86% rename from vendor/github.com/nightlyone/lockfile/.gitignore rename to vendor/github.com/gofrs/flock/.gitignore index 5a05665de..daf913b1b 100644 --- a/vendor/github.com/nightlyone/lockfile/.gitignore +++ b/vendor/github.com/gofrs/flock/.gitignore @@ -7,11 +7,6 @@ _obj _test -# popular temporaries -.err -.out -.diff - # Architecture specific extensions/prefixes *.[568vq] [568vq].out @@ -25,3 +20,5 @@ _cgo_export.* _testmain.go *.exe +*.test +*.prof diff --git a/vendor/github.com/gofrs/flock/.travis.yml b/vendor/github.com/gofrs/flock/.travis.yml new file mode 100644 index 000000000..b16d040fa --- /dev/null +++ b/vendor/github.com/gofrs/flock/.travis.yml @@ -0,0 +1,10 @@ +language: go +go: + - 1.14.x + - 1.15.x +script: go test -v -check.vv -race ./... +sudo: false +notifications: + email: + on_success: never + on_failure: always diff --git a/vendor/github.com/gofrs/flock/LICENSE b/vendor/github.com/gofrs/flock/LICENSE new file mode 100644 index 000000000..8b8ff36fe --- /dev/null +++ b/vendor/github.com/gofrs/flock/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015-2020, Tim Heckman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of gofrs nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gofrs/flock/README.md b/vendor/github.com/gofrs/flock/README.md new file mode 100644 index 000000000..71ce63692 --- /dev/null +++ b/vendor/github.com/gofrs/flock/README.md @@ -0,0 +1,41 @@ +# flock +[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock) +[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock) +[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE) +[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) + +`flock` implements a thread-safe sync.Locker interface for file locking. It also +includes a non-blocking TryLock() function to allow locking without blocking execution. + +## License +`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. + +## Go Compatibility +This package makes use of the `context` package that was introduced in Go 1.7. As such, this +package has an implicit dependency on Go 1.7+. + +## Installation +``` +go get -u github.com/gofrs/flock +``` + +## Usage +```Go +import "github.com/gofrs/flock" + +fileLock := flock.New("/var/lock/go-lock.lock") + +locked, err := fileLock.TryLock() + +if err != nil { + // handle locking error +} + +if locked { + // do work + fileLock.Unlock() +} +``` + +For more detailed usage information take a look at the package API docs on +[GoDoc](https://godoc.org/github.com/gofrs/flock). diff --git a/vendor/github.com/gofrs/flock/appveyor.yml b/vendor/github.com/gofrs/flock/appveyor.yml new file mode 100644 index 000000000..909b4bf7c --- /dev/null +++ b/vendor/github.com/gofrs/flock/appveyor.yml @@ -0,0 +1,25 @@ +version: '{build}' + +build: false +deploy: false + +clone_folder: 'c:\gopath\src\github.com\gofrs\flock' + +environment: + GOPATH: 'c:\gopath' + GOVERSION: '1.15' + +init: + - git config --global core.autocrlf input + +install: + - rmdir c:\go /s /q + - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi + - msiexec /i go%GOVERSION%.windows-amd64.msi /q + - set Path=c:\go\bin;c:\gopath\bin;%Path% + - go version + - go env + +test_script: + - go get -t ./... + - go test -race -v ./... diff --git a/vendor/github.com/gofrs/flock/flock.go b/vendor/github.com/gofrs/flock/flock.go new file mode 100644 index 000000000..95c784ca5 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock.go @@ -0,0 +1,144 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// Package flock implements a thread-safe interface for file locking. +// It also includes a non-blocking TryLock() function to allow locking +// without blocking execution. +// +// Package flock is released under the BSD 3-Clause License. See the LICENSE file +// for more details. +// +// While using this library, remember that the locking behaviors are not +// guaranteed to be the same on each platform. For example, some UNIX-like +// operating systems will transparently convert a shared lock to an exclusive +// lock. If you Unlock() the flock from a location where you believe that you +// have the shared lock, you may accidentally drop the exclusive lock. +package flock + +import ( + "context" + "os" + "runtime" + "sync" + "time" +) + +// Flock is the struct type to handle file locking. All fields are unexported, +// with access to some of the fields provided by getter methods (Path() and Locked()). +type Flock struct { + path string + m sync.RWMutex + fh *os.File + l bool + r bool +} + +// New returns a new instance of *Flock. The only parameter +// it takes is the path to the desired lockfile. +func New(path string) *Flock { + return &Flock{path: path} +} + +// NewFlock returns a new instance of *Flock. The only parameter +// it takes is the path to the desired lockfile. +// +// Deprecated: Use New instead. +func NewFlock(path string) *Flock { + return New(path) +} + +// Close is equivalent to calling Unlock. +// +// This will release the lock and close the underlying file descriptor. +// It will not remove the file from disk, that's up to your application. +func (f *Flock) Close() error { + return f.Unlock() +} + +// Path returns the path as provided in NewFlock(). +func (f *Flock) Path() string { + return f.path +} + +// Locked returns the lock state (locked: true, unlocked: false). +// +// Warning: by the time you use the returned value, the state may have changed. +func (f *Flock) Locked() bool { + f.m.RLock() + defer f.m.RUnlock() + return f.l +} + +// RLocked returns the read lock state (locked: true, unlocked: false). +// +// Warning: by the time you use the returned value, the state may have changed. +func (f *Flock) RLocked() bool { + f.m.RLock() + defer f.m.RUnlock() + return f.r +} + +func (f *Flock) String() string { + return f.path +} + +// TryLockContext repeatedly tries to take an exclusive lock until one of the +// conditions is met: TryLock succeeds, TryLock fails with error, or Context +// Done channel is closed. +func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { + return tryCtx(ctx, f.TryLock, retryDelay) +} + +// TryRLockContext repeatedly tries to take a shared lock until one of the +// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context +// Done channel is closed. +func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { + return tryCtx(ctx, f.TryRLock, retryDelay) +} + +func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Duration) (bool, error) { + if ctx.Err() != nil { + return false, ctx.Err() + } + for { + if ok, err := fn(); ok || err != nil { + return ok, err + } + select { + case <-ctx.Done(): + return false, ctx.Err() + case <-time.After(retryDelay): + // try again + } + } +} + +func (f *Flock) setFh() error { + // open a new os.File instance + // create it if it doesn't exist, and open the file read-only. + flags := os.O_CREATE + if runtime.GOOS == "aix" { + // AIX cannot preform write-lock (ie exclusive) on a + // read-only file. + flags |= os.O_RDWR + } else { + flags |= os.O_RDONLY + } + fh, err := os.OpenFile(f.path, flags, os.FileMode(0600)) + if err != nil { + return err + } + + // set the filehandle on the struct + f.fh = fh + return nil +} + +// ensure the file handle is closed if no lock is held +func (f *Flock) ensureFhState() { + if !f.l && !f.r && f.fh != nil { + f.fh.Close() + f.fh = nil + } +} diff --git a/vendor/github.com/gofrs/flock/flock_aix.go b/vendor/github.com/gofrs/flock/flock_aix.go new file mode 100644 index 000000000..2a1607b2f --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_aix.go @@ -0,0 +1,271 @@ +// Copyright 2019 Tim Heckman. All rights reserved. Use of this source code is +// governed by the BSD 3-Clause license that can be found in the LICENSE file. + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code implements the filelock API using POSIX 'fcntl' locks, which attach +// to an (inode, process) pair rather than a file descriptor. To avoid unlocking +// files prematurely when the same file is opened through different descriptors, +// we allow only one read-lock at a time. +// +// This code is adapted from the Go package: +// cmd/go/internal/lockedfile/internal/filelock + +//+build aix + +package flock + +import ( + "errors" + "io" + "os" + "sync" + "syscall" + + "golang.org/x/sys/unix" +) + +type lockType int16 + +const ( + readLock lockType = unix.F_RDLCK + writeLock lockType = unix.F_WRLCK +) + +type inode = uint64 + +type inodeLock struct { + owner *Flock + queue []<-chan *Flock +} + +var ( + mu sync.Mutex + inodes = map[*Flock]inode{} + locks = map[inode]inodeLock{} +) + +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), this may transparently replace the +// shared lock with an exclusive lock on some UNIX-like operating systems. Be +// careful when using exclusive locks in conjunction with shared locks +// (RLock()), because calling Unlock() may accidentally release the exclusive +// lock that was once a shared lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, writeLock) +} + +// RLock is a blocking call to try and take a shared file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, readLock) +} + +func (f *Flock) lock(locked *bool, flag lockType) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return err + } + defer f.ensureFhState() + } + + if _, err := f.doLock(flag, true); err != nil { + return err + } + + *locked = true + return nil +} + +func (f *Flock) doLock(lt lockType, blocking bool) (bool, error) { + // POSIX locks apply per inode and process, and the lock for an inode is + // released when *any* descriptor for that inode is closed. So we need to + // synchronize access to each inode internally, and must serialize lock and + // unlock calls that refer to the same inode through different descriptors. + fi, err := f.fh.Stat() + if err != nil { + return false, err + } + ino := inode(fi.Sys().(*syscall.Stat_t).Ino) + + mu.Lock() + if i, dup := inodes[f]; dup && i != ino { + mu.Unlock() + return false, &os.PathError{ + Path: f.Path(), + Err: errors.New("inode for file changed since last Lock or RLock"), + } + } + + inodes[f] = ino + + var wait chan *Flock + l := locks[ino] + if l.owner == f { + // This file already owns the lock, but the call may change its lock type. + } else if l.owner == nil { + // No owner: it's ours now. + l.owner = f + } else if !blocking { + // Already owned: cannot take the lock. + mu.Unlock() + return false, nil + } else { + // Already owned: add a channel to wait on. + wait = make(chan *Flock) + l.queue = append(l.queue, wait) + } + locks[ino] = l + mu.Unlock() + + if wait != nil { + wait <- f + } + + err = setlkw(f.fh.Fd(), lt) + + if err != nil { + f.doUnlock() + return false, err + } + + return true, nil +} + +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // if we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + if err := f.doUnlock(); err != nil { + return err + } + + f.fh.Close() + + f.l = false + f.r = false + f.fh = nil + + return nil +} + +func (f *Flock) doUnlock() (err error) { + var owner *Flock + mu.Lock() + ino, ok := inodes[f] + if ok { + owner = locks[ino].owner + } + mu.Unlock() + + if owner == f { + err = setlkw(f.fh.Fd(), unix.F_UNLCK) + } + + mu.Lock() + l := locks[ino] + if len(l.queue) == 0 { + // No waiters: remove the map entry. + delete(locks, ino) + } else { + // The first waiter is sending us their file now. + // Receive it and update the queue. + l.owner = <-l.queue[0] + l.queue = l.queue[1:] + locks[ino] = l + } + delete(inodes, f) + mu.Unlock() + + return err +} + +// TryLock is the preferred function for taking an exclusive file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the exclusive +// file lock, the function will return false instead of waiting for the lock. If +// we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, writeLock) +} + +// TryRLock is the preferred function for taking a shared file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the shared file +// lock, the function will return false instead of waiting for the lock. If we +// get the lock, we also set the *Flock instance as being share-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, readLock) +} + +func (f *Flock) try(locked *bool, flag lockType) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return false, err + } + defer f.ensureFhState() + } + + haslock, err := f.doLock(flag, false) + if err != nil { + return false, err + } + + *locked = haslock + return haslock, nil +} + +// setlkw calls FcntlFlock with F_SETLKW for the entire file indicated by fd. +func setlkw(fd uintptr, lt lockType) error { + for { + err := unix.FcntlFlock(fd, unix.F_SETLKW, &unix.Flock_t{ + Type: int16(lt), + Whence: io.SeekStart, + Start: 0, + Len: 0, // All bytes. + }) + if err != unix.EINTR { + return err + } + } +} diff --git a/vendor/github.com/gofrs/flock/flock_unix.go b/vendor/github.com/gofrs/flock/flock_unix.go new file mode 100644 index 000000000..c315a3e29 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_unix.go @@ -0,0 +1,197 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// +build !aix,!windows + +package flock + +import ( + "os" + "syscall" +) + +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), this may transparently replace the +// shared lock with an exclusive lock on some UNIX-like operating systems. Be +// careful when using exclusive locks in conjunction with shared locks +// (RLock()), because calling Unlock() may accidentally release the exclusive +// lock that was once a shared lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, syscall.LOCK_EX) +} + +// RLock is a blocking call to try and take a shared file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, syscall.LOCK_SH) +} + +func (f *Flock) lock(locked *bool, flag int) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return err + } + defer f.ensureFhState() + } + + if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { + return reopenErr + } + + if !shouldRetry { + return err + } + + if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil { + return err + } + } + + *locked = true + return nil +} + +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // if we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + // mark the file as unlocked + if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil { + return err + } + + f.fh.Close() + + f.l = false + f.r = false + f.fh = nil + + return nil +} + +// TryLock is the preferred function for taking an exclusive file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the exclusive +// file lock, the function will return false instead of waiting for the lock. If +// we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, syscall.LOCK_EX) +} + +// TryRLock is the preferred function for taking a shared file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the shared file +// lock, the function will return false instead of waiting for the lock. If we +// get the lock, we also set the *Flock instance as being share-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, syscall.LOCK_SH) +} + +func (f *Flock) try(locked *bool, flag int) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return false, err + } + defer f.ensureFhState() + } + + var retried bool +retry: + err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB) + + switch err { + case syscall.EWOULDBLOCK: + return false, nil + case nil: + *locked = true + return true, nil + } + if !retried { + if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil { + return false, reopenErr + } else if shouldRetry { + retried = true + goto retry + } + } + + return false, err +} + +// reopenFDOnError determines whether we should reopen the file handle +// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c: +// Since Linux 3.4 (commit 55725513) +// Probably NFSv4 where flock() is emulated by fcntl(). +func (f *Flock) reopenFDOnError(err error) (bool, error) { + if err != syscall.EIO && err != syscall.EBADF { + return false, nil + } + if st, err := f.fh.Stat(); err == nil { + // if the file is able to be read and written + if st.Mode()&0600 == 0600 { + f.fh.Close() + f.fh = nil + + // reopen in read-write mode and set the filehandle + fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600)) + if err != nil { + return false, err + } + f.fh = fh + return true, nil + } + } + + return false, nil +} diff --git a/vendor/github.com/gofrs/flock/flock_winapi.go b/vendor/github.com/gofrs/flock/flock_winapi.go new file mode 100644 index 000000000..fe405a255 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_winapi.go @@ -0,0 +1,76 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// +build windows + +package flock + +import ( + "syscall" + "unsafe" +) + +var ( + kernel32, _ = syscall.LoadLibrary("kernel32.dll") + procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx") + procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx") +) + +const ( + winLockfileFailImmediately = 0x00000001 + winLockfileExclusiveLock = 0x00000002 + winLockfileSharedLock = 0x00000000 +) + +// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows +// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: +// +// > The function requests an exclusive lock. Otherwise, it requests a shared +// > lock. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx + +func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { + r1, _, errNo := syscall.Syscall6( + uintptr(procLockFileEx), + 6, + uintptr(handle), + uintptr(flags), + uintptr(reserved), + uintptr(numberOfBytesToLockLow), + uintptr(numberOfBytesToLockHigh), + uintptr(unsafe.Pointer(offset))) + + if r1 != 1 { + if errNo == 0 { + return false, syscall.EINVAL + } + + return false, errNo + } + + return true, 0 +} + +func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { + r1, _, errNo := syscall.Syscall6( + uintptr(procUnlockFileEx), + 5, + uintptr(handle), + uintptr(reserved), + uintptr(numberOfBytesToLockLow), + uintptr(numberOfBytesToLockHigh), + uintptr(unsafe.Pointer(offset)), + 0) + + if r1 != 1 { + if errNo == 0 { + return false, syscall.EINVAL + } + + return false, errNo + } + + return true, 0 +} diff --git a/vendor/github.com/gofrs/flock/flock_windows.go b/vendor/github.com/gofrs/flock/flock_windows.go new file mode 100644 index 000000000..ddb534cce --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_windows.go @@ -0,0 +1,142 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +package flock + +import ( + "syscall" +) + +// ErrorLockViolation is the error code returned from the Windows syscall when a +// lock would block and you ask to fail immediately. +const ErrorLockViolation syscall.Errno = 0x21 // 33 + +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, winLockfileExclusiveLock) +} + +// RLock is a blocking call to try and take a shared file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, winLockfileSharedLock) +} + +func (f *Flock) lock(locked *bool, flag uint32) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return err + } + defer f.ensureFhState() + } + + if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { + return errNo + } + + *locked = true + return nil +} + +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// UnlockFileEx() on the file and closes the file descriptor. It does not remove +// the file from disk. It's up to your application to do. +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // if we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + // mark the file as unlocked + if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { + return errNo + } + + f.fh.Close() + + f.l = false + f.r = false + f.fh = nil + + return nil +} + +// TryLock is the preferred function for taking an exclusive file lock. This +// function does take a RW-mutex lock before it tries to lock the file, so there +// is the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the exclusive +// file lock, the function will return false instead of waiting for the lock. If +// we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, winLockfileExclusiveLock) +} + +// TryRLock is the preferred function for taking a shared file lock. This +// function does take a RW-mutex lock before it tries to lock the file, so there +// is the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the shared file +// lock, the function will return false instead of waiting for the lock. If we +// get the lock, we also set the *Flock instance as being shared-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, winLockfileSharedLock) +} + +func (f *Flock) try(locked *bool, flag uint32) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return false, err + } + defer f.ensureFhState() + } + + _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{}) + + if errNo > 0 { + if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING { + return false, nil + } + + return false, errNo + } + + *locked = true + + return true, nil +} diff --git a/vendor/github.com/nightlyone/lockfile/.gitmodules b/vendor/github.com/nightlyone/lockfile/.gitmodules deleted file mode 100644 index 6faa9e346..000000000 --- a/vendor/github.com/nightlyone/lockfile/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "git-hooks"] - path = git-hooks - url = https://github.com/nightlyone/git-hooks diff --git a/vendor/github.com/nightlyone/lockfile/.travis.yml b/vendor/github.com/nightlyone/lockfile/.travis.yml deleted file mode 100644 index 4f0af474a..000000000 --- a/vendor/github.com/nightlyone/lockfile/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go -go: - - 1.13 - - 1.14 - - tip - -# Only test commits to production branch and all pull requests -branches: - only: - - master - -matrix: - allow_failures: - - go: tip diff --git a/vendor/github.com/nightlyone/lockfile/LICENSE b/vendor/github.com/nightlyone/lockfile/LICENSE deleted file mode 100644 index eb5b80468..000000000 --- a/vendor/github.com/nightlyone/lockfile/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2012 Ingo Oeser - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/nightlyone/lockfile/README.md b/vendor/github.com/nightlyone/lockfile/README.md deleted file mode 100644 index c35235cda..000000000 --- a/vendor/github.com/nightlyone/lockfile/README.md +++ /dev/null @@ -1,52 +0,0 @@ -lockfile -========= -Handle locking via pid files. - -[![Build Status Unix][1]][2] -[![Build status Windows][3]][4] - -[1]: https://secure.travis-ci.org/nightlyone/lockfile.png -[2]: https://travis-ci.org/nightlyone/lockfile -[3]: https://ci.appveyor.com/api/projects/status/7mojkmauj81uvp8u/branch/master?svg=true -[4]: https://ci.appveyor.com/project/nightlyone/lockfile/branch/master - - - -install -------- -Install [Go 1][5], either [from source][6] or [with a prepackaged binary][7]. -For Windows suport, Go 1.4 or newer is required. - -Then run - - go get github.com/nightlyone/lockfile - -[5]: http://golang.org -[6]: http://golang.org/doc/install/source -[7]: http://golang.org/doc/install - -LICENSE -------- -MIT - -documentation -------------- -[package documentation at godoc.org](http://godoc.org/github.com/nightlyone/lockfile) - -install -------------------- - go get github.com/nightlyone/lockfile - - -contributing -============ - -Contributions are welcome. Please open an issue or send me a pull request for a dedicated branch. -Make sure the git commit hooks show it works. - -git commit hooks ------------------------ -enable commit hooks via - - cd .git ; rm -rf hooks; ln -s ../git-hooks hooks ; cd .. - diff --git a/vendor/github.com/nightlyone/lockfile/appveyor.yml b/vendor/github.com/nightlyone/lockfile/appveyor.yml deleted file mode 100644 index cf72a58b1..000000000 --- a/vendor/github.com/nightlyone/lockfile/appveyor.yml +++ /dev/null @@ -1,12 +0,0 @@ -clone_folder: c:\gopath\src\github.com\nightlyone\lockfile - -environment: - GOPATH: c:\gopath - -install: - - go version - - go env - - go get -v -t ./... - -build_script: - - go test -v ./... diff --git a/vendor/github.com/nightlyone/lockfile/go.mod b/vendor/github.com/nightlyone/lockfile/go.mod deleted file mode 100644 index 5133ff715..000000000 --- a/vendor/github.com/nightlyone/lockfile/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/nightlyone/lockfile - -go 1.11 diff --git a/vendor/github.com/nightlyone/lockfile/lockfile.go b/vendor/github.com/nightlyone/lockfile/lockfile.go deleted file mode 100644 index 76bdfbe09..000000000 --- a/vendor/github.com/nightlyone/lockfile/lockfile.go +++ /dev/null @@ -1,222 +0,0 @@ -// Package lockfile handles pid file based locking. -// While a sync.Mutex helps against concurrency issues within a single process, -// this package is designed to help against concurrency issues between cooperating processes -// or serializing multiple invocations of the same process. You can also combine sync.Mutex -// with Lockfile in order to serialize an action between different goroutines in a single program -// and also multiple invocations of this program. -package lockfile - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" -) - -// Lockfile is a pid file which can be locked -type Lockfile string - -// TemporaryError is a type of error where a retry after a random amount of sleep should help to mitigate it. -type TemporaryError string - -func (t TemporaryError) Error() string { return string(t) } - -// Temporary returns always true. -// It exists, so you can detect it via -// if te, ok := err.(interface{ Temporary() bool }); ok { -// fmt.Println("I am a temporary error situation, so wait and retry") -// } -func (t TemporaryError) Temporary() bool { return true } - -// Various errors returned by this package -var ( - ErrBusy = TemporaryError("Locked by other process") // If you get this, retry after a short sleep might help - ErrNotExist = TemporaryError("Lockfile created, but doesn't exist") // If you get this, retry after a short sleep might help - ErrNeedAbsPath = errors.New("Lockfiles must be given as absolute path names") - ErrInvalidPid = errors.New("Lockfile contains invalid pid for system") - ErrDeadOwner = errors.New("Lockfile contains pid of process not existent on this system anymore") - ErrRogueDeletion = errors.New("Lockfile owned by me has been removed unexpectedly") -) - -// New describes a new filename located at the given absolute path. -func New(path string) (Lockfile, error) { - if !filepath.IsAbs(path) { - return Lockfile(""), ErrNeedAbsPath - } - - return Lockfile(path), nil -} - -// GetOwner returns who owns the lockfile. -func (l Lockfile) GetOwner() (*os.Process, error) { - name := string(l) - - // Ok, see, if we have a stale lockfile here - content, err := ioutil.ReadFile(name) - if err != nil { - return nil, err - } - - // try hard for pids. If no pid, the lockfile is junk anyway and we delete it. - pid, err := scanPidLine(content) - if err != nil { - return nil, err - } - - running, err := isRunning(pid) - if err != nil { - return nil, err - } - - if running { - proc, err := os.FindProcess(pid) - if err != nil { - return nil, err - } - - return proc, nil - } - - return nil, ErrDeadOwner -} - -// TryLock tries to own the lock. -// It Returns nil, if successful and and error describing the reason, it didn't work out. -// Please note, that existing lockfiles containing pids of dead processes -// and lockfiles containing no pid at all are simply deleted. -func (l Lockfile) TryLock() error { - name := string(l) - - // This has been checked by New already. If we trigger here, - // the caller didn't use New and re-implemented it's functionality badly. - // So panic, that he might find this easily during testing. - if !filepath.IsAbs(name) { - panic(ErrNeedAbsPath) - } - - tmplock, cleanup, err := makePidFile(name, os.Getpid()) - if err != nil { - return err - } - - defer cleanup() - - // EEXIST and similar error codes, caught by os.IsExist, are intentionally ignored, - // as it means that someone was faster creating this link - // and ignoring this kind of error is part of the algorithm. - // Then we will probably fail the pid owner check later, if this process is still alive. - // We cannot ignore ALL errors, since failure to support hard links, disk full - // as well as many other errors can happen to a filesystem operation - // and we really want to abort on those. - if err := os.Link(tmplock, name); err != nil { - if !os.IsExist(err) { - return err - } - } - - fiTmp, err := os.Lstat(tmplock) - if err != nil { - return err - } - - fiLock, err := os.Lstat(name) - if err != nil { - // tell user that a retry would be a good idea - if os.IsNotExist(err) { - return ErrNotExist - } - - return err - } - - // Success - if os.SameFile(fiTmp, fiLock) { - return nil - } - - proc, err := l.GetOwner() - switch err { - default: - // Other errors -> defensively fail and let caller handle this - return err - case nil: - if proc.Pid != os.Getpid() { - return ErrBusy - } - case ErrDeadOwner, ErrInvalidPid: // cases we can fix below - } - - // clean stale/invalid lockfile - err = os.Remove(name) - if err != nil { - // If it doesn't exist, then it doesn't matter who removed it. - if !os.IsNotExist(err) { - return err - } - } - - // now that the stale lockfile is gone, let's recurse - return l.TryLock() -} - -// Unlock a lock again, if we owned it. Returns any error that happened during release of lock. -func (l Lockfile) Unlock() error { - proc, err := l.GetOwner() - switch err { - case ErrInvalidPid, ErrDeadOwner: - return ErrRogueDeletion - case nil: - if proc.Pid == os.Getpid() { - // we really own it, so let's remove it. - return os.Remove(string(l)) - } - // Not owned by me, so don't delete it. - return ErrRogueDeletion - default: - // This is an application error or system error. - // So give a better error for logging here. - if os.IsNotExist(err) { - return ErrRogueDeletion - } - // Other errors -> defensively fail and let caller handle this - return err - } -} - -func scanPidLine(content []byte) (int, error) { - if len(content) == 0 { - return 0, ErrInvalidPid - } - - var pid int - if _, err := fmt.Sscanln(string(content), &pid); err != nil { - return 0, ErrInvalidPid - } - - if pid <= 0 { - return 0, ErrInvalidPid - } - - return pid, nil -} - -func makePidFile(name string, pid int) (tmpname string, cleanup func(), err error) { - tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".") - if err != nil { - return "", nil, err - } - - cleanup = func() { - _ = tmplock.Close() - _ = os.Remove(tmplock.Name()) - } - - if _, err := io.WriteString(tmplock, fmt.Sprintf("%d\n", pid)); err != nil { - cleanup() // Do cleanup here, so call doesn't have to. - return "", nil, err - } - - return tmplock.Name(), cleanup, nil -} diff --git a/vendor/github.com/nightlyone/lockfile/lockfile_unix.go b/vendor/github.com/nightlyone/lockfile/lockfile_unix.go deleted file mode 100644 index d43e9b5ee..000000000 --- a/vendor/github.com/nightlyone/lockfile/lockfile_unix.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris aix - -package lockfile - -import ( - "os" - "syscall" -) - -func isRunning(pid int) (bool, error) { - proc, err := os.FindProcess(pid) - if err != nil { - return false, err - } - - if err := proc.Signal(syscall.Signal(0)); err != nil { - return false, nil - } - - return true, nil -} diff --git a/vendor/github.com/nightlyone/lockfile/lockfile_windows.go b/vendor/github.com/nightlyone/lockfile/lockfile_windows.go deleted file mode 100644 index 482bd91d7..000000000 --- a/vendor/github.com/nightlyone/lockfile/lockfile_windows.go +++ /dev/null @@ -1,30 +0,0 @@ -package lockfile - -import ( - "syscall" -) - -//For some reason these consts don't exist in syscall. -const ( - error_invalid_parameter = 87 - code_still_active = 259 -) - -func isRunning(pid int) (bool, error) { - procHnd, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, true, uint32(pid)) - if err != nil { - if scerr, ok := err.(syscall.Errno); ok { - if uintptr(scerr) == error_invalid_parameter { - return false, nil - } - } - } - - var code uint32 - err = syscall.GetExitCodeProcess(procHnd, &code) - if err != nil { - return false, err - } - - return code == code_still_active, nil -} diff --git a/vendor/github.com/qinqon/kube-admission-webhook/pkg/webhook/server/server.go b/vendor/github.com/qinqon/kube-admission-webhook/pkg/webhook/server/server.go deleted file mode 100644 index 33f46a2de..000000000 --- a/vendor/github.com/qinqon/kube-admission-webhook/pkg/webhook/server/server.go +++ /dev/null @@ -1,162 +0,0 @@ -package server - -import ( - "context" - "io/ioutil" - "os" - "path" - "time" - - "github.com/go-logr/logr" - "github.com/pkg/errors" - - corev1 "k8s.io/api/core/v1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/healthz" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/runtime/inject" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - "github.com/qinqon/kube-admission-webhook/pkg/certificate" - "github.com/qinqon/kube-admission-webhook/pkg/certificate/triple" -) - -type Server struct { - webhookServer *webhook.Server - certManager *certificate.Manager - log logr.Logger -} - -type ServerModifier func(w *webhook.Server) - -// Add creates a new Conditions Mutating Webhook and adds it to the Manager. The Manager will set fields on the Webhook -// and Start it when the Manager is Started. -func New(client client.Client, certificateOpts certificate.Options, serverOpts ...ServerModifier) (*Server, error) { - certManager, err := certificate.NewManager(client, certificateOpts) - if err != nil { - return nil, errors.Wrap(err, "failed constructing certificate manager") - } - - s := &Server{ - webhookServer: &webhook.Server{ - Port: 8443, - CertDir: "/etc/webhook/certs/", - }, - certManager: certManager, - log: logf.Log.WithName("webhook/server"), - } - s.UpdateOpts(serverOpts...) - s.webhookServer.Register("/readyz", healthz.CheckHandler{Checker: healthz.Ping}) - return s, nil -} - -func WithHook(path string, hook *webhook.Admission) ServerModifier { - return func(s *webhook.Server) { - s.Register(path, hook) - } -} - -func WithPort(port int) ServerModifier { - return func(s *webhook.Server) { - s.Port = port - } -} - -func WithCertDir(certDir string) ServerModifier { - return func(s *webhook.Server) { - s.CertDir = certDir - } -} - -//updates Server parameters using ServerModifier functions. Once the manager is started these parameters cannot be updated -func (s *Server) UpdateOpts(serverOpts ...ServerModifier) { - for _, serverOpt := range serverOpts { - serverOpt(s.webhookServer) - } -} - -func (s *Server) Add(mgr manager.Manager) error { - err := s.certManager.Add(mgr) - if err != nil { - return errors.Wrap(err, "failed adding certificate manager to controller-runtime manager") - } - err = mgr.Add(s) - if err != nil { - return errors.Wrap(err, "failed adding webhook server to controller-runtime manager") - } - return nil -} - -func (s *Server) checkTLS() error { - - keyPath := path.Join(s.webhookServer.CertDir, corev1.TLSPrivateKeyKey) - _, err := os.Stat(keyPath) - if err != nil { - return errors.Wrap(err, "failed checking TLS key file stats") - } - - certsPath := path.Join(s.webhookServer.CertDir, corev1.TLSCertKey) - _, err = os.Stat(certsPath) - if err != nil { - return errors.Wrap(err, "failed checking TLS cert file stats") - } - - key, err := ioutil.ReadFile(path.Join(s.webhookServer.CertDir, corev1.TLSPrivateKeyKey)) - if err != nil { - return errors.Wrap(err, "failed reading for TLS key") - } - - certPEM, err := ioutil.ReadFile(path.Join(s.webhookServer.CertDir, corev1.TLSCertKey)) - if err != nil { - return errors.Wrap(err, "failed reading for TLS cert") - } - - caPEM, err := s.certManager.CABundle() - if err != nil { - return errors.Wrap(err, "failed to retrieve CA cert") - } - - err = triple.VerifyTLS(certPEM, key, caPEM) - if err != nil { - return errors.Wrapf(err, "failed verifying %s/%s", certsPath, keyPath) - } - - return nil -} - -func (s *Server) waitForTLSReadiness() error { - return wait.PollImmediate(5*time.Second, 5*time.Minute, func() (bool, error) { - err := s.checkTLS() - if err != nil { - utilruntime.HandleError(err) - return false, nil - } - return true, nil - }) -} - -func (s *Server) Start(ctx context.Context) error { - s.log.Info("Starting nodenetworkconfigurationpolicy webhook server") - - err := s.waitForTLSReadiness() - if err != nil { - return errors.Wrap(err, "failed watting for ready TLS key/cert") - } - - err = s.webhookServer.Start(ctx) - if err != nil { - return errors.Wrap(err, "failed starting webhook server") - } - return nil -} - -func (s *Server) InjectFunc(f inject.Func) error { - return s.webhookServer.InjectFunc(f) -} - -func (s *Server) NeedLeaderElection() bool { - return s.webhookServer.NeedLeaderElection() -} diff --git a/vendor/modules.txt b/vendor/modules.txt index eaaeef5e2..d4b603566 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -311,6 +311,9 @@ github.com/gobwas/glob/util/runes github.com/gobwas/glob/util/strings # github.com/godbus/dbus v4.1.0+incompatible github.com/godbus/dbus +# github.com/gofrs/flock v0.8.0 +## explicit +github.com/gofrs/flock # github.com/gogo/protobuf v1.3.1 github.com/gogo/protobuf/gogoproto github.com/gogo/protobuf/proto @@ -466,7 +469,6 @@ github.com/modern-go/reflect2 github.com/morikuni/aec # github.com/nightlyone/lockfile v1.0.0 ## explicit -github.com/nightlyone/lockfile # github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 github.com/nozzle/throttler # github.com/nxadm/tail v1.4.4 @@ -691,7 +693,6 @@ github.com/prometheus/procfs/internal/util ## explicit github.com/qinqon/kube-admission-webhook/pkg/certificate github.com/qinqon/kube-admission-webhook/pkg/certificate/triple -github.com/qinqon/kube-admission-webhook/pkg/webhook/server # github.com/rogpeppe/go-internal v1.5.0 github.com/rogpeppe/go-internal/modfile github.com/rogpeppe/go-internal/module