Class: Chef::RunLock

Inherits:
Object show all
Includes:
Mixin::CreatePath
Defined in:
lib/chef/run_lock.rb

Overview

Chef::RunLock

Provides an interface for acquiring and releasing a system-wide exclusive lock.

Used by Chef::Client to ensure only one instance of chef-client (or solo) is modifying the system at a time.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Mixin::CreatePath

#create_path

Constructor Details

#initialize(config) ⇒ RunLock

Create a new instance of RunLock

Arguments

  • config:

    This will generally be the Chef::Config, but any Hash-like

    object with Symbol keys will work. See ‘Parameters’ section.

Parameters/Config

  • :lockfile:

    if set, this will be used as the full path to the lockfile.

  • :file_cache_path:

    if :lockfile is not set, the lock file will be

    named “chef-client-running.pid” and be placed in the directory given by :file_cache_path



44
45
46
47
# File 'lib/chef/run_lock.rb', line 44

def initialize(config)
  @runlock_file = config[:lockfile] || "#{config[:file_cache_path]}/chef-client-running.pid"
  @runlock = nil
end

Instance Attribute Details

#runlockObject (readonly)

Returns the value of attribute runlock.



32
33
34
# File 'lib/chef/run_lock.rb', line 32

def runlock
  @runlock
end

#runlock_fileObject (readonly)

Returns the value of attribute runlock_file.



33
34
35
# File 'lib/chef/run_lock.rb', line 33

def runlock_file
  @runlock_file
end

Instance Method Details

#acquireObject

Acquire the system-wide lock. Will block indefinitely if another process already has the lock.

Each call to acquire should have a corresponding call to #release.

The implementation is based on File#flock (see also: flock(2)).



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/chef/run_lock.rb', line 55

def acquire
  # ensure the runlock_file path exists
  create_path(File.dirname(runlock_file))
  @runlock = File.open(runlock_file,'w+')
  # if we support FD_CLOEXEC (linux, !windows), then use it.
  # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not ruby-1.8.7/1.9.3
  if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
    runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
  end
  unless runlock.flock(File::LOCK_EX|File::LOCK_NB)
    # Another chef client running...
    runpid = runlock.read.strip.chomp
    Chef::Log.warn("Chef client #{runpid} is running, will wait for it to finish and then run.")
    runlock.flock(File::LOCK_EX)
  end
  # We grabbed the run lock.  Save the pid.
  runlock.truncate(0)
  runlock.rewind # truncate doesn't reset position to 0.
  runlock.write(Process.pid.to_s)
end

#releaseObject

Release the system-wide lock.



77
78
79
80
81
82
83
84
85
86
# File 'lib/chef/run_lock.rb', line 77

def release
  if runlock
    runlock.flock(File::LOCK_UN)
    runlock.close
    # Don't unlink the pid file, if another chef-client was waiting, it
    # won't be recreated. Better to leave a "dead" pid file than not have
    # it available if you need to break the lock.
    reset
  end
end