summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/util/ini_file.rb75
-rw-r--r--lib/puppet/util/ini_file/section.rb24
-rw-r--r--spec/unit/puppet/provider/ini_setting/ruby_spec.rb204
-rw-r--r--tests/ini_setting.pp8
4 files changed, 294 insertions, 17 deletions
diff --git a/lib/puppet/util/ini_file.rb b/lib/puppet/util/ini_file.rb
index 52ad32c..0409db2 100644
--- a/lib/puppet/util/ini_file.rb
+++ b/lib/puppet/util/ini_file.rb
@@ -6,7 +6,7 @@ module Util
class IniFile
SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:]+)\]\s*$/
- SETTING_REGEX = /^\s*([\w\d\.\\\/\-]+)\s*=\s*([\S\s]*\S)\s*$/
+ SETTING_REGEX = /^(\s*)([\w\d\.\\\/\-]+)(\s*=\s*)([\S\s]*\S)\s*$/
def initialize(path, key_val_separator = ' = ')
@path = path
@@ -30,7 +30,7 @@ module Util
def set_value(section_name, setting, value)
unless (@sections_hash.has_key?(section_name))
- add_section(Section.new(section_name, nil, nil, nil))
+ add_section(Section.new(section_name, nil, nil, nil, nil))
end
section = @sections_hash[section_name]
@@ -64,21 +64,62 @@ module Util
def save
File.open(@path, 'w') do |fh|
- @section_names.each do |name|
+ @section_names.each_index do |index|
+ name = @section_names[index]
section = @sections_hash[name]
- if section.start_line.nil?
+ # We need a buffer to cache lines that are only whitespace
+ whitespace_buffer = []
+
+ if (section.is_new_section?) && (! section.is_global?)
fh.puts("\n[#{section.name}]")
- elsif ! section.end_line.nil?
+ end
+
+ if ! section.is_new_section?
+ # write all of the pre-existing settings
(section.start_line..section.end_line).each do |line_num|
- fh.puts(lines[line_num])
+ line = lines[line_num]
+
+ # We buffer any lines that are only whitespace so that
+ # if they are at the end of a section, we can insert
+ # any new settings *before* the final chunk of whitespace
+ # lines.
+ if (line =~ /^\s*$/)
+ whitespace_buffer << line
+ else
+ # If we get here, we've found a non-whitespace line.
+ # We'll flush any cached whitespace lines before we
+ # write it.
+ flush_buffer_to_file(whitespace_buffer, fh)
+ fh.puts(line)
+ end
end
end
+ # write new settings, if there are any
section.additional_settings.each_pair do |key, value|
- fh.puts("#{key}#{@key_val_separator}#{value}")
+ fh.puts("#{' ' * (section.indentation || 0)}#{key}#{@key_val_separator}#{value}")
+ end
+
+ if (whitespace_buffer.length > 0)
+ flush_buffer_to_file(whitespace_buffer, fh)
+ else
+ # We get here if there were no blank lines at the end of the
+ # section.
+ #
+ # If we are adding a new section with a new setting,
+ # and if there are more sections that come after this one,
+ # we'll write one blank line just so that there is a little
+ # whitespace between the sections.
+ #if (section.end_line.nil? &&
+ if (section.is_new_section? &&
+ (section.additional_settings.length > 0) &&
+ (index < @section_names.length - 1))
+ fh.puts("")
+ end
end
+
end
end
end
@@ -111,12 +152,15 @@ module Util
def read_section(name, start_line, line_iter)
settings = {}
end_line_num = nil
+ min_indentation = nil
while true
line, line_num = line_iter.peek
if (line_num.nil? or match = SECTION_REGEX.match(line))
- return Section.new(name, start_line, end_line_num, settings)
+ return Section.new(name, start_line, end_line_num, settings, min_indentation)
elsif (match = SETTING_REGEX.match(line))
- settings[match[1]] = match[2]
+ settings[match[2]] = match[4]
+ indentation = match[1].length
+ min_indentation = [indentation, min_indentation || indentation].min
end
end_line_num = line_num
line_iter.next
@@ -126,8 +170,8 @@ module Util
def update_line(section, setting, value)
(section.start_line..section.end_line).each do |line_num|
if (match = SETTING_REGEX.match(lines[line_num]))
- if (match[1] == setting)
- lines[line_num] = "#{setting}#{@key_val_separator}#{value}"
+ if (match[2] == setting)
+ lines[line_num] = "#{match[1]}#{match[2]}#{match[3]}#{value}"
end
end
end
@@ -136,7 +180,7 @@ module Util
def remove_line(section, setting)
(section.start_line..section.end_line).each do |line_num|
if (match = SETTING_REGEX.match(lines[line_num]))
- if (match[1] == setting)
+ if (match[2] == setting)
lines.delete_at(line_num)
end
end
@@ -173,6 +217,13 @@ module Util
end
end
+ def flush_buffer_to_file(buffer, fh)
+ if buffer.length > 0
+ buffer.each { |l| fh.puts(l) }
+ buffer.clear
+ end
+ end
+
end
end
end
diff --git a/lib/puppet/util/ini_file/section.rb b/lib/puppet/util/ini_file/section.rb
index 16f19d3..d7ff159 100644
--- a/lib/puppet/util/ini_file/section.rb
+++ b/lib/puppet/util/ini_file/section.rb
@@ -2,15 +2,35 @@ module Puppet
module Util
class IniFile
class Section
- def initialize(name, start_line, end_line, settings)
+ # Some implementation details:
+ #
+ # * `name` will be set to the empty string for the 'global' section.
+ # * there will always be a 'global' section, with a `start_line` of 0,
+ # but if the file actually begins with a real section header on
+ # the first line, then the 'global' section will have an
+ # `end_line` of `nil`.
+ # * `start_line` and `end_line` will be set to `nil` for a new non-global
+ # section.
+ def initialize(name, start_line, end_line, settings, indentation)
@name = name
@start_line = start_line
@end_line = end_line
@existing_settings = settings.nil? ? {} : settings
@additional_settings = {}
+ @indentation = indentation
end
- attr_reader :name, :start_line, :end_line, :additional_settings
+ attr_reader :name, :start_line, :end_line, :additional_settings, :indentation
+
+ def is_global?()
+ @name == ''
+ end
+
+ def is_new_section?()
+ # a new section (global or named) will always have `end_line`
+ # set to `nil`
+ @end_line.nil?
+ end
def get_value(setting_name)
@existing_settings[setting_name] || @additional_settings[setting_name]
diff --git a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
index 8b2f8e5..19db4c7 100644
--- a/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
+++ b/spec/unit/puppet/provider/ini_setting/ruby_spec.rb
@@ -123,7 +123,7 @@ master = true
[section2]
foo= foovalue2
-baz = bazvalue2
+baz=bazvalue2
url = http://192.168.1.1:8080
[section:sub]
subby=bar
@@ -154,7 +154,7 @@ foo= foovalue2
baz=bazvalue
url = http://192.168.1.1:8080
[section:sub]
-subby = foo
+subby=foo
#another comment
; yet another comment
EOS
@@ -329,7 +329,7 @@ foo = http://192.168.1.1:8080
provider.value=('yippee')
validate_file(<<-EOS
# This is a comment
-foo = yippee
+foo=yippee
[section2]
foo = http://192.168.1.1:8080
; yet another comment
@@ -361,6 +361,7 @@ foo = http://192.168.1.1:8080
provider.create
validate_file(<<-EOS
foo = yippee
+
[section2]
foo = http://192.168.1.1:8080
EOS
@@ -533,4 +534,201 @@ subby=bar
end
end
+
+ context "when dealing with indentation in sections" do
+ let(:orig_content) {
+ <<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ EOS
+ }
+
+ it "should add a missing setting at the correct indentation when the header is aligned" do
+ resource = Puppet::Type::Ini_setting.new(common_params.merge(
+ :section => 'section1', :setting => 'yahoo', :value => 'yippee'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_nil
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+ yahoo = yippee
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ EOS
+ )
+ end
+
+ it "should update an existing setting at the correct indentation when the header is aligned" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section1', :setting => 'bar', :value => 'barvalue2'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_true
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue2
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ EOS
+ )
+ end
+
+ it "should add a missing setting at the correct indentation when the header is not aligned" do
+ resource = Puppet::Type::Ini_setting.new(common_params.merge(
+ :section => 'section2', :setting => 'yahoo', :value => 'yippee'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_nil
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+ yahoo = yippee
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ EOS
+ )
+ end
+
+ it "should update an existing setting at the correct indentation when the header is not aligned" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section2', :setting => 'baz', :value => 'bazvalue2'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_true
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue2
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ EOS
+ )
+ end
+
+ it "should add a missing setting at the min indentation when the section is not aligned" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section:sub', :setting => 'yahoo', :value => 'yippee'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_nil
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam
+ ; yet another comment
+ yahoo = yippee
+ EOS
+ )
+ end
+
+ it "should update an existing setting at the previous indentation when the section is not aligned" do
+ resource = Puppet::Type::Ini_setting.new(
+ common_params.merge(:section => 'section:sub', :setting => 'fleezy', :value => 'flam2'))
+ provider = described_class.new(resource)
+ provider.exists?.should be_true
+ provider.create
+ validate_file(<<-EOS
+# This is a comment
+ [section1]
+ ; This is also a comment
+ foo=foovalue
+
+ bar = barvalue
+ master = true
+
+[section2]
+ foo= foovalue2
+ baz=bazvalue
+ url = http://192.168.1.1:8080
+[section:sub]
+ subby=bar
+ #another comment
+ fleezy = flam2
+ ; yet another comment
+ EOS
+ )
+ end
+
+ end
+
end
diff --git a/tests/ini_setting.pp b/tests/ini_setting.pp
index 6fd7686..5e2f169 100644
--- a/tests/ini_setting.pp
+++ b/tests/ini_setting.pp
@@ -15,3 +15,11 @@ ini_setting { "sample setting2":
ensure => present,
require => Ini_setting["sample setting"],
}
+
+ini_setting { "sample setting3":
+ path => '/tmp/foo.ini',
+ section => 'bar',
+ setting => 'bazsetting',
+ ensure => absent,
+ require => Ini_setting["sample setting2"],
+}