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(lockfile) ⇒ RunLock

Create a new instance of RunLock

Arguments

  • :lockfile:

    the full path to the lockfile.



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

def initialize(lockfile)
  @runlock_file = lockfile
  @runlock = nil
  @mutex = nil
end

Instance Attribute Details

#mutexObject (readonly)

Returns the value of attribute mutex.



36
37
38
# File 'lib/chef/run_lock.rb', line 36

def mutex
  @mutex
end

#runlockObject (readonly)

Returns the value of attribute runlock.



35
36
37
# File 'lib/chef/run_lock.rb', line 35

def runlock
  @runlock
end

#runlock_fileObject (readonly)

Returns the value of attribute runlock_file.



37
38
39
# File 'lib/chef/run_lock.rb', line 37

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)).

Either acquire() or test() methods should be called in order to get the ownership of run_lock.



57
58
59
# File 'lib/chef/run_lock.rb', line 57

def acquire
  wait unless test
end

#releaseObject

Release the system-wide lock.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/chef/run_lock.rb', line 114

def release
  if runlock
    if Chef::Platform.windows?
      mutex.release
    else
      runlock.flock(File::LOCK_UN)
    end
    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

#save_pidObject



104
105
106
107
108
109
110
111
# File 'lib/chef/run_lock.rb', line 104

def save_pid
  runlock.truncate(0)
  runlock.rewind # truncate doesn't reset position to 0.
  runlock.write(Process.pid.to_s)
  # flush the file fsync flushes the system buffers
  # in addition to ruby buffers
  runlock.fsync
end

#testObject

Tests and if successful acquires the system-wide lock. Returns true if the lock is acquired, false otherwise.

Either acquire() or test() methods should be called in order to get the ownership of run_lock.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/chef/run_lock.rb', line 67

def test
  # ensure the runlock_file path exists
  create_path(File.dirname(runlock_file))
  @runlock = File.open(runlock_file,'a+')

  if Chef::Platform.windows?
    acquire_win32_mutex
  else
    # If we support FD_CLOEXEC, 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
    # Flock will return 0 if it can acquire the lock otherwise it
    # will return false
    if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
      true
    else
      false
    end
  end
end

#waitObject

Waits until acquiring the system-wide lock.



94
95
96
97
98
99
100
101
102
# File 'lib/chef/run_lock.rb', line 94

def wait
  runpid = runlock.read.strip.chomp
  Chef::Log.warn("Chef client #{runpid} is running, will wait for it to finish and then run.")
  if Chef::Platform.windows?
    mutex.wait
  else
    runlock.flock(File::LOCK_EX)
  end
end