summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-07-21 21:16:09 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-07-21 21:16:09 +0000
commit9e61510ac96cc53b2fbc58efa969499eb0c0c11f (patch)
treef72e42e9d6cbdf4b45095087d927c5e8c4fa6fea
parent310b3a11eec563c4687041f9ae1a0b894571bc05 (diff)
downloadpuppet-9e61510ac96cc53b2fbc58efa969499eb0c0c11f.tar.gz
puppet-9e61510ac96cc53b2fbc58efa969499eb0c0c11f.tar.xz
puppet-9e61510ac96cc53b2fbc58efa969499eb0c0c11f.zip
Fixing #77. As I feared, this was a pretty complicated fix; I had to add a lot of infrastructure to both ParsedFile and Config. All config files now have a timer created for them, and by default they check for file changes every 15 seconds. If there is a change, they get rid of values set by the file (but not set on the cli) and set the new values, then the re-use all of the sections, so that any changed directories or whatever get recreated.
This is still not a 100% solution, since things like open files could still be messed with, but I think this is about as close as we are going to get. git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1420 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--lib/puppet/config.rb95
-rwxr-xr-xlib/puppet/parsedfile.rb2
-rwxr-xr-xtest/other/config.rb110
3 files changed, 181 insertions, 26 deletions
diff --git a/lib/puppet/config.rb b/lib/puppet/config.rb
index f70201cee..c697545ce 100644
--- a/lib/puppet/config.rb
+++ b/lib/puppet/config.rb
@@ -9,10 +9,14 @@ class Config
@@sync = Sync.new
+ attr_reader :file, :timer
# Retrieve a config value
def [](param)
param = symbolize(param)
+
+ # Yay, recursion.
+ self.reparse() unless param == :filetimeout
if @config.include?(param)
if @config[param]
val = @config[param].value
@@ -25,14 +29,19 @@ class Config
# Set a config value. This doesn't set the defaults, it sets the value itself.
def []=(param, value)
- param = symbolize(param)
- unless @config.include?(param)
- raise Puppet::Error, "Unknown configuration parameter %s" % param.inspect
- end
- unless @order.include?(param)
- @order << param
+ @@sync.synchronize do # yay, thread-safe
+ param = symbolize(param)
+ unless @config.include?(param)
+ raise Puppet::Error,
+ "Unknown configuration parameter %s" % param.inspect
+ end
+ unless @order.include?(param)
+ @order << param
+ end
+ @config[param].value = value
end
- @config[param].value = value
+
+ return value
end
# A simplified equality operator.
@@ -91,14 +100,19 @@ class Config
end
end
- # Remove all set values.
+ # Remove all set values, potentially skipping cli values.
def clear(exceptcli = false)
@config.each { |name, obj|
unless exceptcli and obj.setbycli
obj.clear
end
}
- @used = []
+
+ # Don't clear the 'used' in this case, since it's a config file reparse,
+ # and we want to retain this info.
+ unless exceptcli
+ @used = []
+ end
end
# This is mostly just used for testing.
@@ -140,7 +154,7 @@ class Config
# Handle a command-line argument.
def handlearg(opt, value = nil)
- value = mungearg(value)
+ value = mungearg(value) if value
str = opt.sub(/^--/,'')
bool = true
newstr = str.sub(/^no-/, '')
@@ -201,7 +215,7 @@ class Config
when /^true$/i: true
when /^\d+$/i: Integer(value)
else
- value
+ value.gsub(/^["']|["']$/,'')
end
end
@@ -219,21 +233,30 @@ class Config
def parse(file)
text = nil
+ if file.is_a? Puppet::ParsedFile
+ @file = file
+ else
+ @file = Puppet::ParsedFile.new(file)
+ end
+
+ # Create a timer so that this.
+ settimer()
+
begin
- text = File.read(file)
+ text = File.read(@file.file)
rescue Errno::ENOENT
raise Puppet::Error, "No such file %s" % file
rescue Errno::EACCES
raise Puppet::Error, "Permission denied to file %s" % file
end
- # Store it for later, in a way that we can test and such.
- @file = Puppet::ParsedFile.new(file)
-
@values = Hash.new { |names, name|
names[name] = {}
}
+ # Get rid of the values set by the file, keeping cli values.
+ self.clear(true)
+
section = "puppet"
metas = %w{owner group mode}
values = Hash.new { |hash, key| hash[key] = {} }
@@ -321,6 +344,25 @@ class Config
}
end
+ # Reparse our config file, if necessary.
+ def reparse
+ if defined? @file and @file.changed?
+ Puppet.notice "Reparsing %s" % @file.file
+ parse(@file)
+ reuse()
+ end
+ end
+
+ def reuse
+ return unless defined? @used
+ @@sync.synchronize do # yay, thread-safe
+ @used.each do |section|
+ @used.delete(section)
+ self.use(section)
+ end
+ end
+ end
+
# Get a list of objects per section
def sectionlist
sectionlist = []
@@ -418,6 +460,19 @@ class Config
}
end
+ # Create a timer to check whether the file should be reparsed.
+ def settimer
+ if Puppet[:filetimeout] > 0
+ @timer = Puppet.newtimer(
+ :interval => Puppet[:filetimeout],
+ :tolerance => 1,
+ :start? => true
+ ) do
+ self.reparse()
+ end
+ end
+ end
+
def symbolize(param)
case param
when String: return param.intern
@@ -497,16 +552,6 @@ Generated on #{Time.now}.
return manifest
end
- def reuse
- return unless defined? @used
- @@sync.synchronize do # yay, thread-safe
- @used.each do |section|
- @used.delete(section)
- self.use(section)
- end
- end
- end
-
# Create the necessary objects to use a section. This is idempotent;
# you can 'use' a section as many times as you want.
def use(*sections)
diff --git a/lib/puppet/parsedfile.rb b/lib/puppet/parsedfile.rb
index c5168b2b1..f256ca522 100755
--- a/lib/puppet/parsedfile.rb
+++ b/lib/puppet/parsedfile.rb
@@ -19,7 +19,7 @@ module Puppet
# be reparsed
def changed?
# Don't actually stat the file more often than filetimeout.
- if Time.now - @statted > Puppet[:filetimeout]
+ if Time.now - @statted >= Puppet[:filetimeout]
tmp = stamp()
if tmp == @tstamp
diff --git a/test/other/config.rb b/test/other/config.rb
index 2b3c985a4..33dad6892 100755
--- a/test/other/config.rb
+++ b/test/other/config.rb
@@ -695,7 +695,117 @@ inttest = 27
end
end
+ # Make sure we correctly reparse our config files but don't lose CLI values.
def test_reparse
+ Puppet[:filetimeout] = 0
+
+ config = mkconfig()
+ config.setdefaults(:mysection, :default => ["default", "yay"])
+ config.setdefaults(:mysection, :clichange => ["clichange", "yay"])
+ config.setdefaults(:mysection, :filechange => ["filechange", "yay"])
+
+ file = tempfile()
+ # Set one parameter in the file
+ File.open(file, "w") { |f|
+ f.puts %{[mysection]\nfilechange = filevalue}
+ }
+ assert_nothing_raised {
+ config.parse(file)
+ }
+
+ # Set another "from the cli"
+ assert_nothing_raised {
+ config.handlearg("clichange", "clivalue")
+ }
+
+ # And leave the other unset
+ assert_equal("default", config[:default])
+ assert_equal("filevalue", config[:filechange])
+ assert_equal("clivalue", config[:clichange])
+
+ # Now rewrite the file
+ File.open(file, "w") { |f|
+ f.puts %{[mysection]\nfilechange = newvalue}
+ }
+
+ cfile = config.file
+ cfile.send("tstamp=".intern, Time.now - 50)
+
+ # And check all of the values
+ assert_equal("default", config[:default])
+ assert_equal("clivalue", config[:clichange])
+ assert_equal("newvalue", config[:filechange])
+ end
+
+ def test_parse_removes_quotes
+ config = mkconfig()
+ config.setdefaults(:mysection, :singleq => ["single", "yay"])
+ config.setdefaults(:mysection, :doubleq => ["double", "yay"])
+ config.setdefaults(:mysection, :none => ["noquote", "yay"])
+ config.setdefaults(:mysection, :middle => ["midquote", "yay"])
+
+ file = tempfile()
+ # Set one parameter in the file
+ File.open(file, "w") { |f|
+ f.puts %{[mysection]\n
+ singleq = 'one'
+ doubleq = "one"
+ none = one
+ middle = mid"quote
+}
+ }
+
+ assert_nothing_raised {
+ config.parse(file)
+ }
+
+ %w{singleq doubleq none}.each do |p|
+ assert_equal("one", config[p], "%s did not match" % p)
+ end
+ assert_equal('mid"quote', config["middle"], "middle did not match")
+ end
+
+ def test_timer
+ Puppet[:filetimeout] = 0.1
+ origpath = tempfile()
+ config = mkconfig()
+ config.setdefaults(:mysection, :paramdir => [tempfile(), "yay"])
+
+ file = tempfile()
+ # Set one parameter in the file
+ File.open(file, "w") { |f|
+ f.puts %{[mysection]\n
+ paramdir = #{origpath}
+}
+ }
+
+ assert_nothing_raised {
+ config.parse(file)
+ config.use(:mysection)
+ }
+
+ assert(FileTest.directory?(origpath), "dir did not get created")
+
+ # Now start the timer
+ assert_nothing_raised {
+ EventLoop.current.monitor_timer config.timer
+ }
+
+ newpath = tempfile()
+
+ File.open(file, "w") { |f|
+ f.puts %{[mysection]\n
+ paramdir = #{newpath}
+}
+ }
+ config.file.send("tstamp=".intern, Time.now - 50)
+ sleep 1
+
+ assert_equal(newpath, config["paramdir"],
+ "File did not get reparsed from timer")
+ assert(FileTest.directory?(newpath), "new dir did not get created")
+
+
end
end