summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/util/ini_file.rb63
-rw-r--r--lib/puppet/util/ini_file/section.rb25
-rw-r--r--spec/unit/puppet/provider/ini_setting/ruby_spec.rb80
3 files changed, 168 insertions, 0 deletions
diff --git a/lib/puppet/util/ini_file.rb b/lib/puppet/util/ini_file.rb
index 0409db2..90b0c7b 100644
--- a/lib/puppet/util/ini_file.rb
+++ b/lib/puppet/util/ini_file.rb
@@ -7,6 +7,7 @@ module Util
SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:]+)\]\s*$/
SETTING_REGEX = /^(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
+ COMMENTED_SETTING_REGEX = /^(\s*)[#;]+(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
def initialize(path, key_val_separator = ' = ')
@path = path
@@ -34,9 +35,31 @@ module Util
end
section = @sections_hash[section_name]
+
if (section.has_existing_setting?(setting))
update_line(section, setting, value)
section.update_existing_setting(setting, value)
+ elsif result = find_commented_setting(section, setting)
+ # So, this stanza is a bit of a hack. What we're trying
+ # to do here is this: for settings that don't already
+ # exist, we want to take a quick peek to see if there
+ # is a commented-out version of them in the section.
+ # If so, we'd prefer to add the setting directly after
+ # the commented line, rather than at the end of the section.
+
+ # If we get here then we found a commented line, so we
+ # call "insert_inline_setting_line" to update the lines array
+ insert_inline_setting_line(result, section, setting, value)
+
+ # Then, we need to tell the setting object that we hacked
+ # in an inline setting
+ section.insert_inline_setting(setting, value)
+
+ # Finally, we need to update all of the start/end line
+ # numbers for all of the sections *after* the one that
+ # was modified.
+ section_index = @section_names.index(section_name)
+ increment_section_line_numbers(section_index + 1)
else
section.set_additional_setting(setting, value)
end
@@ -206,6 +229,35 @@ module Util
File.readlines(path)
end
+ # This utility method scans through the lines for a section looking for
+ # commented-out versions of a setting. It returns `nil` if it doesn't
+ # find one. If it does find one, then it returns a hash containing
+ # two keys:
+ #
+ # :line_num - the line number that contains the commented version
+ # of the setting
+ # :match - the ruby regular expression match object, which can
+ # be used to mimic the whitespace from the comment line
+ def find_commented_setting(section, setting)
+ return nil if section.is_new_section?
+ (section.start_line..section.end_line).each do |line_num|
+ if (match = COMMENTED_SETTING_REGEX.match(lines[line_num]))
+ if (match[3] == setting)
+ return { :match => match, :line_num => line_num }
+ end
+ end
+ end
+ nil
+ end
+
+ # This utility method is for inserting a line into the existing
+ # lines array. The `result` argument is expected to be in the
+ # format of the return value of `find_commented_setting`.
+ def insert_inline_setting_line(result, section, setting, value)
+ line_num = result[:line_num]
+ match = result[:match]
+ lines.insert(line_num + 1, "#{' ' * section.indentation}#{setting}#{match[4]}#{value}")
+ end
# Utility method; given a section index (index into the @section_names
# array), decrement the start/end line numbers for that section and all
@@ -217,6 +269,17 @@ module Util
end
end
+ # Utility method; given a section index (index into the @section_names
+ # array), increment the start/end line numbers for that section and all
+ # all of the other sections that appear *after* the specified section.
+ def increment_section_line_numbers(section_index)
+ @section_names[section_index..(@section_names.length - 1)].each do |name|
+ section = @sections_hash[name]
+ section.increment_line_nums
+ end
+ end
+
+
def flush_buffer_to_file(buffer, fh)
if buffer.length > 0
buffer.each { |l| fh.puts(l) }
diff --git a/lib/puppet/util/ini_file/section.rb b/lib/puppet/util/ini_file/section.rb
index d7ff159..ba1a783 100644
--- a/lib/puppet/util/ini_file/section.rb
+++ b/lib/puppet/util/ini_file/section.rb
@@ -52,6 +52,19 @@ class IniFile
end
end
+ # This is a hacky method; it's basically called when we need to insert
+ # a new setting but we don't want it to appear at the very end of the
+ # section. Instead we hack it into the existing settings list and
+ # increment our end_line number--this assumes that the caller (`ini_file`)
+ # is doing some babysitting w/rt the other sections and the actual data
+ # of the lines.
+ def insert_inline_setting(setting_name, value)
+ @existing_settings[setting_name] = value
+ if @end_line
+ @end_line = @end_line + 1
+ end
+ end
+
def set_additional_setting(setting_name, value)
@additional_settings[setting_name] = value
end
@@ -68,6 +81,18 @@ class IniFile
end
end
+ # Increment the start and end line numbers for the section (if they are
+ # defined); this is intended to be called when an inline setting is added
+ # to a section that comes before this section in the ini file.
+ def increment_line_nums()
+ if @start_line
+ @start_line = @start_line + 1
+ end
+ if @end_line
+ @end_line = @end_line + 1
+ end
+ end
+
end
end
end
diff --git a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
index 19db4c7..4f8bdc1 100644
--- a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
+++ b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
@@ -731,4 +731,84 @@ subby=bar
end
+
+ context "when dealing settings that have a commented version present" do
+ let(:orig_content) {
+ <<-EOS
+ [section1]
+ # foo=foovalue
+ bar=barvalue
+ foo = foovalue2
+
+[section2]
+# foo = foovalue
+;bar=barvalue
+blah = blah
+ EOS
+ }
+
+ it "should add a new setting below a commented version of that setting" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section2', :setting => 'foo', :value => 'foo3'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_false
+ provider.create
+ validate_file(<<-EOS
+ [section1]
+ # foo=foovalue
+ bar=barvalue
+ foo = foovalue2
+
+[section2]
+# foo = foovalue
+foo = foo3
+;bar=barvalue
+blah = blah
+ EOS
+ )
+ end
+
+ it "should update an existing setting in place, even if there is a commented version of that setting" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section1', :setting => 'foo', :value => 'foo3'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_true
+ provider.create
+ validate_file(<<-EOS
+ [section1]
+ # foo=foovalue
+ bar=barvalue
+ foo = foo3
+
+[section2]
+# foo = foovalue
+;bar=barvalue
+blah = blah
+ EOS
+ )
+ end
+
+ it "should add a new setting below a commented version of that setting, respecting semicolons as comments" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section2', :setting => 'bar', :value => 'bar3'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_false
+ provider.create
+ validate_file(<<-EOS
+ [section1]
+ # foo=foovalue
+ bar=barvalue
+ foo = foovalue2
+
+[section2]
+# foo = foovalue
+;bar=barvalue
+bar=bar3
+blah = blah
+ EOS
+ )
+ end
+
+ end
+
end