From 3fd5bb377ba1d8f111481a2d5cbb1467c5c6f520 Mon Sep 17 00:00:00 2001 From: Ayan George Date: Fri, 29 Sep 2017 17:20:59 -0400 Subject: [PATCH] Disable file locking when DEPNOLOCK set (#1206) * Disable file locking when DEPNOLOCK set * Add DisableLocking bool members to Ctx and gps.SourceManagerConfig structs. This effectively communicates DEPNOLOCK from the shell, to Ctx, to SourceManager. The member is named DisableLocking to make its zero-value useful. * Add locker interface which implements TryLock(), Unlock(), and GetOwner() which lockfile.Lockfile alredy adheres to. This interface replaces the new type for the lf member of the SourceMgr struct. * Add a FalseLocker type which adheres to the Locker interface which does nothing. * Conditionally set the lf member of SourceMgr to either an instance of lockfile.Lockfile or FalseLocker depending on the value of SourceManagerConfig.DisableLocking. Signed-off-by: Ayan George * Revert stray edit. Signed-off-by: Ayan George * Improve comment for DisableLocking Signed-off-by: Ayan George * Fix comment type-os * Fix comment type-os Signed-off-by: Ayan George * Fix yet more type-os. Signed-off-by: Ayan George --- cmd/dep/main.go | 7 +++-- context.go | 16 +++++----- internal/gps/source_manager.go | 53 ++++++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/cmd/dep/main.go b/cmd/dep/main.go index bb66c8e639..f465197028 100644 --- a/cmd/dep/main.go +++ b/cmd/dep/main.go @@ -147,9 +147,10 @@ func (c *Config) Run() (exitCode int) { // Set up dep context. ctx := &dep.Ctx{ - Out: outLogger, - Err: errLogger, - Verbose: *verbose, + Out: outLogger, + Err: errLogger, + Verbose: *verbose, + DisableLocking: getEnv(c.Env, "DEPNOLOCK") != "", } GOPATHS := filepath.SplitList(getEnv(c.Env, "GOPATH")) diff --git a/context.go b/context.go index f0a80473ab..ba8007360e 100644 --- a/context.go +++ b/context.go @@ -34,11 +34,12 @@ import ( // } // type Ctx struct { - WorkingDir string // Where to execute. - GOPATH string // Selected Go path, containing WorkingDir. - GOPATHs []string // Other Go paths. - Out, Err *log.Logger // Required loggers. - Verbose bool // Enables more verbose logging. + WorkingDir string // Where to execute. + GOPATH string // Selected Go path, containing WorkingDir. + GOPATHs []string // Other Go paths. + Out, Err *log.Logger // Required loggers. + Verbose bool // Enables more verbose logging. + DisableLocking bool // When set, no lock file will be created to protect against simultaneous dep processes. } // SetPaths sets the WorkingDir and GOPATHs fields. If GOPATHs is empty, then @@ -87,8 +88,9 @@ func defaultGOPATH() string { // initialized to log to the receiver's logger. func (c *Ctx) SourceManager() (*gps.SourceMgr, error) { return gps.NewSourceManager(gps.SourceManagerConfig{ - Cachedir: filepath.Join(c.GOPATH, "pkg", "dep"), - Logger: c.Out, + Cachedir: filepath.Join(c.GOPATH, "pkg", "dep"), + Logger: c.Out, + DisableLocking: c.DisableLocking, }) } diff --git a/internal/gps/source_manager.go b/internal/gps/source_manager.go index 38b3dd8281..23f3c1c8b5 100644 --- a/internal/gps/source_manager.go +++ b/internal/gps/source_manager.go @@ -28,6 +28,41 @@ import ( // Used to compute a friendly filepath from a URL-shaped input. var sanitizer = strings.NewReplacer("-", "--", ":", "-", "/", "-", "+", "-") +// A locker is responsible for preventing multiple instances of dep from +// interfering with one-another. +// +// Currently, anything that can either TryLock(), Unlock(), or GetOwner() +// satifies that need. +type locker interface { + TryLock() error + Unlock() error + GetOwner() (*os.Process, error) +} + +// A falselocker adheres to the locker interface and its purpose is to quietly +// fail to lock when the DEPNOLOCK environment variable is set. +// +// This allows dep to run on systems where file locking doesn't work -- +// particularly those that use union mount type filesystems that don't +// implement hard links or fnctl() style locking. +type falseLocker struct{} + +// Always returns an error to indicate there's no current ower PID for our +// lock. +func (fl falseLocker) GetOwner() (*os.Process, error) { + return nil, fmt.Errorf("falseLocker always fails") +} + +// Does nothing and returns a nil error so caller beleives locking succeeded. +func (fl falseLocker) TryLock() error { + return nil +} + +// Does nothing and returns a nil error so caller beleives unlocking succeeded. +func (fl falseLocker) Unlock() error { + return nil +} + // A SourceManager is responsible for retrieving, managing, and interrogating // source repositories. Its primary purpose is to serve the needs of a Solver, // but it is handy for other purposes, as well. @@ -121,7 +156,7 @@ func (p ProjectAnalyzerInfo) String() string { // tools; control via dependency injection is intended to be sufficient. type SourceMgr struct { cachedir string // path to root of cache dir - lf *lockfile.Lockfile // handle for the sm lock file on disk + lf locker // handle for the sm lock file on disk suprvsr *supervisor // subsystem that supervises running calls/io cancelAll context.CancelFunc // cancel func to kill all running work deduceCoord *deductionCoordinator // subsystem that manages import path deduction @@ -142,8 +177,9 @@ var _ SourceManager = &SourceMgr{} // SourceManagerConfig holds configuration information for creating SourceMgrs. type SourceManagerConfig struct { - Cachedir string // Where to store local instances of upstream sources. - Logger *log.Logger // Optional info/warn logger. Discards if nil. + Cachedir string // Where to store local instances of upstream sources. + Logger *log.Logger // Optional info/warn logger. Discards if nil. + DisableLocking bool // True if the SourceManager should NOT use a lock file to protect the Cachedir from multiple processes. } // NewSourceManager produces an instance of gps's built-in SourceManager. @@ -175,7 +211,14 @@ func NewSourceManager(c SourceManagerConfig) (*SourceMgr, error) { // we can spin on. glpath := filepath.Join(c.Cachedir, "sm.lock") - lockfile, err := lockfile.New(glpath) + + lockfile, err := func() (locker, error) { + if c.DisableLocking { + return falseLocker{}, nil + } + return lockfile.New(glpath) + }() + if err != nil { return nil, CouldNotCreateLockError{ Path: glpath, @@ -238,7 +281,7 @@ func NewSourceManager(c SourceManagerConfig) (*SourceMgr, error) { sm := &SourceMgr{ cachedir: c.Cachedir, - lf: &lockfile, + lf: lockfile, suprvsr: superv, cancelAll: cf, deduceCoord: deducer,