diff --git a/REFERENCE.md b/REFERENCE.md index 16534f4..3d78a59 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,263 +1,271 @@ # Reference ## Table of Contents **Resource types** * [`ini_setting`](#ini_setting): ini_settings is used to manage a single setting in an INI file * [`ini_subsetting`](#ini_subsetting): ini_subsettings is used to manage multiple values in a setting in an INI file **Functions** * [`create_ini_settings`](#create_ini_settings): DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. * [`inifile::create_ini_settings`](#inifilecreate_ini_settings): This function is used to create a set of ini_setting resources from a hash ## Resource types ### ini_setting ini_settings is used to manage a single setting in an INI file #### Properties The following properties are available in the `ini_setting` type. ##### `ensure` Valid values: present, absent Ensurable method handles modeling creation. It creates an ensure property Default value: present ##### `value` The value of the setting to be defined. #### Parameters The following parameters are available in the `ini_setting` type. ##### `name` namevar An arbitrary name used as the identity of the resource. ##### `section` The name of the section in the ini file in which the setting should be defined. Default value: '' ##### `setting` The name of the setting to be defined. ##### `force_new_section_creation` Valid values: `true`, `false`, yes, no Create setting only if the section exists Default value: `true` ##### `path` The ini file Puppet will ensure contains the specified setting. ##### `show_diff` Valid values: `true`, md5, `false` Whether to display differences when the setting changes. Default value: `true` ##### `key_val_separator` The separator string to use between each setting name and value. Default value: = ##### `section_prefix` The prefix to the section name\'s header. Default value: [ ##### `section_suffix` The suffix to the section name\'s header. Default value: ] ##### `indent_char` The character to indent new settings with. Default value: ##### `indent_width` The number of indent_chars to use to indent a new setting. ##### `refreshonly` Valid values: `true`, `false`, yes, no A flag indicating whether or not the ini_setting should be updated only when called as part of a refresh event Default value: `false` ### ini_subsetting ini_subsettings is used to manage multiple values in a setting in an INI file #### Properties The following properties are available in the `ini_subsetting` type. ##### `ensure` Valid values: present, absent Ensurable method handles modeling creation. It creates an ensure property Default value: present ##### `value` The value of the subsetting to be defined. #### Parameters The following parameters are available in the `ini_subsetting` type. ##### `name` namevar An arbitrary name used as the identity of the resource. ##### `section` The name of the section in the ini file in which the setting should be defined. Default value: '' ##### `setting` The name of the setting to be defined. ##### `subsetting` The name of the subsetting to be defined. ##### `subsetting_separator` The separator string between subsettings. Defaults to the empty string. Default value: ##### `subsetting_key_val_separator` The separator string between the subsetting name and its value. Defaults to the empty string. Default value: '' ##### `path` The ini file Puppet will ensure contains the specified setting. ##### `show_diff` Valid values: `true`, md5, `false` Whether to display differences when the setting changes. Default value: `true` ##### `key_val_separator` The separator string to use between each setting name and value. Default value: = ##### `quote_char` The character used to quote the entire value of the setting. Valid values are '', '\"' and \"'\" Default value: '' ##### `use_exact_match` Valid values: `true`, `false` Set to true if your subsettings don\'t have values and you want to use exact matches to determine if the subsetting exists. Default value: `false` ##### `insert_type` Valid values: start, end, before, after, index Where the new subsetting item should be inserted * :start - insert at the beginning of the line. * :end - insert at the end of the line (default). * :before - insert before the specified element if possible. * :after - insert after the specified element if possible. * :index - insert at the specified index number. Default value: end ##### `insert_value` The value for the insert types which require one. +##### `delete_if_empty` + +Valid values: `true`, `false` + +Set to true to delete the parent setting when the subsetting is empty instead of writing an empty string + +Default value: `false` + ## Functions ### create_ini_settings Type: Ruby 4.x API DEPRECATED. Use the namespaced function [`inifile::create_ini_settings`](#inifilecreate_ini_settings) instead. #### `create_ini_settings(Any *$args)` The create_ini_settings function. Returns: `Any` ##### `*args` Data type: `Any` ### inifile::create_ini_settings Type: Ruby 4.x API This function is used to create a set of ini_setting resources from a hash #### `inifile::create_ini_settings(Hash $settings, Optional[Hash] $defaults)` The inifile::create_ini_settings function. Returns: `Any` ##### `settings` Data type: `Hash` A hash of settings you want to create ini_setting resources from ##### `defaults` Data type: `Optional[Hash]` A hash of defaults you would like to use in the ini_setting resources diff --git a/lib/puppet/provider/ini_subsetting/ruby.rb b/lib/puppet/provider/ini_subsetting/ruby.rb index 50b7a8a..3c32a29 100644 --- a/lib/puppet/provider/ini_subsetting/ruby.rb +++ b/lib/puppet/provider/ini_subsetting/ruby.rb @@ -1,90 +1,94 @@ require File.expand_path('../../../util/ini_file', __FILE__) require File.expand_path('../../../util/setting_value', __FILE__) Puppet::Type.type(:ini_subsetting).provide(:ruby) do desc ' Creates new ini_subsetting file, a specific config file with a provider that uses this as its parent and implements the method self.file_path, and that will provide the value for the path to the ini file.' def exists? setting_value.get_subsetting_value(subsetting, resource[:use_exact_match]) end def create setting_value.add_subsetting( subsetting, resource[:value], resource[:use_exact_match], resource[:insert_type], resource[:insert_value] ) ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) ini_file.save @ini_file = nil @setting_value = nil end def destroy setting_value.remove_subsetting(subsetting, resource[:use_exact_match]) - ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) + if setting_value.get_value.empty? && resource[:delete_if_empty] + ini_file.remove_setting(section, setting) + else + ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) + end ini_file.save @ini_file = nil @setting_value = nil end def value setting_value.get_subsetting_value(subsetting) end def value=(value) setting_value.add_subsetting( subsetting, value, resource[:use_exact_match], resource[:insert_type], resource[:insert_value] ) ini_file.set_value(section, setting, key_val_separator, setting_value.get_value) ini_file.save end def section resource[:section] end def setting resource[:setting] end def subsetting resource[:subsetting] end def subsetting_separator resource[:subsetting_separator] end def file_path resource[:path] end def key_val_separator resource[:key_val_separator] || '=' end def subsetting_key_val_separator resource[:subsetting_key_val_separator] || '' end def quote_char resource[:quote_char] end private def ini_file @ini_file ||= Puppet::Util::IniFile.new(file_path, key_val_separator) end def setting_value @setting_value ||= Puppet::Util::SettingValue.new( ini_file.get_value(section, setting), subsetting_separator, quote_char, subsetting_key_val_separator ) end end diff --git a/lib/puppet/type/ini_subsetting.rb b/lib/puppet/type/ini_subsetting.rb index 072c56c..b4d1625 100644 --- a/lib/puppet/type/ini_subsetting.rb +++ b/lib/puppet/type/ini_subsetting.rb @@ -1,126 +1,132 @@ require 'digest/md5' Puppet::Type.newtype(:ini_subsetting) do desc 'ini_subsettings is used to manage multiple values in a setting in an INI file' ensurable do desc 'Ensurable method handles modeling creation. It creates an ensure property' defaultvalues defaultto :present end def munge_boolean_md5(value) case value when true, :true, 'true', :yes, 'yes' :true when false, :false, 'false', :no, 'no' :false when :md5, 'md5' :md5 else raise(_('expected a boolean value or :md5')) end end newparam(:name, namevar: true) do desc 'An arbitrary name used as the identity of the resource.' end newparam(:section) do desc 'The name of the section in the ini file in which the setting should be defined.' defaultto('') end newparam(:setting) do desc 'The name of the setting to be defined.' end newparam(:subsetting) do desc 'The name of the subsetting to be defined.' end newparam(:subsetting_separator) do desc 'The separator string between subsettings. Defaults to the empty string.' defaultto(' ') end newparam(:subsetting_key_val_separator) do desc 'The separator string between the subsetting name and its value. Defaults to the empty string.' defaultto('') end newparam(:path) do desc 'The ini file Puppet will ensure contains the specified setting.' validate do |value| unless Puppet::Util.absolute_path?(value) raise(Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: value }) end end end newparam(:show_diff) do desc 'Whether to display differences when the setting changes.' defaultto :true newvalues(:true, :md5, :false) munge do |value| @resource.munge_boolean_md5(value) end end newparam(:key_val_separator) do desc 'The separator string to use between each setting name and value.' defaultto(' = ') end newparam(:quote_char) do desc "The character used to quote the entire value of the setting. Valid values are '', '\"' and \"'\"" defaultto('') validate do |value| unless value =~ %r{^["']?$} raise Puppet::Error, _(%q(:quote_char valid values are '', '"' and "'")) end end end newparam(:use_exact_match) do desc 'Set to true if your subsettings don\'t have values and you want to use exact matches to determine if the subsetting exists.' newvalues(:true, :false) defaultto(:false) end newproperty(:value) do desc 'The value of the subsetting to be defined.' def should_to_s(newvalue) if @resource[:show_diff] == :true && Puppet[:show_diff] newvalue elsif @resource[:show_diff] == :md5 && Puppet[:show_diff] '{md5}' + Digest::MD5.hexdigest(newvalue.to_s) else '[redacted sensitive information]' end end def is_to_s(value) # rubocop:disable Style/PredicateName : Changing breaks the code (./.bundle/gems/gems/puppet-5.3.3-universal-darwin/lib/puppet/parameter.rb:525:in `to_s') should_to_s(value) end end newparam(:insert_type) do desc <<-eof Where the new subsetting item should be inserted * :start - insert at the beginning of the line. * :end - insert at the end of the line (default). * :before - insert before the specified element if possible. * :after - insert after the specified element if possible. * :index - insert at the specified index number. eof newvalues(:start, :end, :before, :after, :index) defaultto(:end) end newparam(:insert_value) do desc 'The value for the insert types which require one.' end + + newparam(:delete_if_empty) do + desc 'Set to true to delete the parent setting when the subsetting is empty instead of writing an empty string' + newvalues(:true, :false) + defaultto(:false) + end end diff --git a/spec/acceptance/ini_subsetting_spec.rb b/spec/acceptance/ini_subsetting_spec.rb index 417a2cc..2465d4d 100644 --- a/spec/acceptance/ini_subsetting_spec.rb +++ b/spec/acceptance/ini_subsetting_spec.rb @@ -1,250 +1,289 @@ require 'spec_helper_acceptance' describe 'ini_subsetting resource' do basedir = setup_test_directory after :all do run_shell("rm #{basedir}/*.ini", expect_failures: true) end shared_examples 'has_content' do |path, pp, content| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest twice' do idempotent_apply(pp) end describe file(path) do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match content } end end end shared_examples 'has_error' do |path, pp, error| before :all do run_shell("rm #{path}", expect_failures: true) end it 'applies the manifest and gets a failure message' do expect(apply_manifest(pp, expect_failures: true).stderr).to match(error) end describe file(path) do it { is_expected.not_to be_file } end end describe 'ensure, section, setting, subsetting, & value parameters => present with subsections' do pp = <<-EOS ini_subsetting { 'ensure => present for alpha': ensure => present, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'alpha', value => 'bet', } ini_subsetting { 'ensure => present for beta': ensure => present, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'beta', value => 'trons', require => Ini_subsetting['ensure => present for alpha'], } EOS describe file("#{basedir}/ini_subsetting.ini") do it_behaves_like 'has_content', "#{basedir}/ini_subsetting.ini", pp, %r{\[one\]\Rkey = alphabet betatrons} end end describe 'ensure => absent' do pp = <<-EOS ini_subsetting { 'ensure => absent for subsetting': ensure => absent, path => "#{basedir}/ini_subsetting.ini", section => 'one', setting => 'key', subsetting => 'alpha', } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_subsetting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match %r{\[one\]} } it { is_expected.to match %r{key = betatrons} } it { is_expected.not_to match %r{alphabet} } end end end + describe 'delete_if_empty => true' do + pp = <<-EOS + ini_subsetting { 'ensure => absent for alpha': + ensure => absent, + path => "#{basedir}/ini_subsetting.ini", + section => 'one', + setting => 'key', + subsetting => 'alpha', + delete_if_empty => true, + } + ini_subsetting { 'ensure => absent for beta': + ensure => absent, + path => "#{basedir}/ini_subsetting.ini", + section => 'one', + setting => 'key', + subsetting => 'beta', + delete_if_empty => true, + } + EOS + + it 'applies the manifest twice' do + run_shell("echo -e [one]\\\\nkey = alphabet betatrons > #{basedir}/ini_subsetting.ini", expect_failures: true) + idempotent_apply(pp) + end + + describe file("#{basedir}/ini_subsetting.ini") do + it { is_expected.to be_file } + + describe '#content' do + subject { super().content } + + it { is_expected.not_to match %r{\[one\]} } + it { is_expected.not_to match %r{key =} } + it { is_expected.not_to match %r{alphabet} } + it { is_expected.not_to match %r{betatrons} } + end + end + end + describe 'quote_char' do { ['-Xmx'] => %r{args=""}, ['-Xmx', '256m'] => %r{args=-Xmx256m}, ['-Xmx', '512m'] => %r{args="-Xmx512m"}, ['-Xms', '256m'] => %r{args="-Xmx256m -Xms256m"}, }.each do |parameter, content| context %(with '#{parameter.first}' #{(parameter.length > 1) ? '=> \'' << parameter[1] << '\'' : 'absent'} makes '#{content}') do path = File.join(basedir, 'ini_subsetting.ini') before :all do ipp = <<-MANIFEST - file { '#{path}': - content => "[java]\nargs=-Xmx256m", - force => true, - } - MANIFEST + file { '#{path}': + content => "[java]\nargs=-Xmx256m", + force => true, + } + MANIFEST apply_manifest(ipp) end after :all do run_shell("cat #{path}", expect_failures: true) run_shell("rm #{path}", expect_failures: true) end pp = <<-EOS ini_subsetting { '#{parameter.first}': ensure => #{(parameter.length > 1) ? 'present' : 'absent'}, path => '#{path}', section => 'java', setting => 'args', quote_char => '"', subsetting => '#{parameter.first}', value => '#{(parameter.length > 1) ? parameter[1] : ''}' } EOS it 'applies the manifest twice' do idempotent_apply(pp) end describe file("#{basedir}/ini_subsetting.ini") do it { is_expected.to be_file } describe '#content' do subject { super().content } it { is_expected.to match content } end end end end end describe 'show_diff parameter and logging:' do setup_puppet_config_file [{ value: 'initial_value', matcher: 'created', show_diff: true }, { value: 'public_value', matcher: %r{initial_value.*public_value}, show_diff: true }, { value: 'secret_value', matcher: %r{redacted sensitive information.*redacted sensitive information}, show_diff: false }, { value: 'md5_value', matcher: %r{\{md5\}881671aa2bbc680bc530c4353125052b.*\{md5\}ed0903a7fa5de7886ca1a7a9ad06cf51}, show_diff: :md5 }].each do |i| - pp = <<-EOS + pp = <<-EOS ini_subsetting { 'test_show_diff': ensure => present, section => 'test', setting => 'something', subsetting => 'xxx', value => '#{i[:value]}', path => "#{basedir}/test_show_diff.ini", show_diff => #{i[:show_diff]} } - EOS + EOS - context "show_diff => #{i[:show_diff]}" do - res = apply_manifest(pp, expect_changes: true) - it 'applies manifest and expects changed value to be logged in proper form' do - expect(res.stdout).to match(i[:matcher]) - end - it 'applies manifest and expects changed value to be logged in proper form #optional test' do - expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true - end - end - end + context "show_diff => #{i[:show_diff]}" do + res = apply_manifest(pp, expect_changes: true) + it 'applies manifest and expects changed value to be logged in proper form' do + expect(res.stdout).to match(i[:matcher]) + end + it 'applies manifest and expects changed value to be logged in proper form #optional test' do + expect(res.stdout).not_to match(i[:value]) unless i[:show_diff] == true + end + end + end end describe 'insert types:' do [ { insert_type: :start, content: %r{d a b c}, }, { insert_type: :end, content: %r{a b c d}, }, { insert_type: :before, insert_value: 'c', content: %r{a b d c}, }, { insert_type: :after, insert_value: 'a', content: %r{a d b c}, }, { insert_type: :index, insert_value: 2, content: %r{a b d c}, }, ].each do |params| context "with '#{params[:insert_type]}' makes '#{params[:content]}'" do pp = <<-EOS ini_subsetting { "a": ensure => present, section => 'one', setting => 'two', subsetting => 'a', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "b": ensure => present, section => 'one', setting => 'two', subsetting => 'b', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "c": ensure => present, section => 'one', setting => 'two', subsetting => 'c', path => "#{basedir}/insert_types.ini", } -> ini_subsetting { "insert makes #{params[:content]}": ensure => present, section => 'one', setting => 'two', subsetting => 'd', path => "#{basedir}/insert_types.ini", insert_type => '#{params[:insert_type]}', insert_value => '#{params[:insert_value]}', } EOS it_behaves_like 'has_content', "#{basedir}/insert_types.ini", pp, params[:content] end end end end diff --git a/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb b/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb index 17738db..d7a6986 100644 --- a/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb +++ b/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb @@ -1,324 +1,361 @@ require 'spec_helper' require 'puppet' provider_class = Puppet::Type.type(:ini_subsetting).provider(:ruby) describe provider_class do include PuppetlabsSpec::Files let(:tmpfile) { tmpfilename('ini_setting_test') } def validate_file(expected_content, tmpfile) expect(File.read(tmpfile)).to eq expected_content end before :each do File.open(tmpfile, 'w') do |fh| fh.write(orig_content) end end context 'when ensuring that a subsetting is present' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: '', key_val_separator: '=', setting: 'JAVA_ARGS', } end let(:orig_content) do <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS end expected_content_one = <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" EOS it 'adds a missing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element at the beginning of the line' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :start)) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS JAVA_ARGS="-Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof -Xms128m" EOS it 'adds a missing subsetting element at the end of the line' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :end)) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element after the given item' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :after, insert_value: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_four, tmpfile) end expected_content_five = <<-EOS JAVA_ARGS="-Xms128m -Xmx192m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element before the given item' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :before, insert_value: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_five, tmpfile) end expected_content_six = <<-EOS JAVA_ARGS="-Xmx192m -Xms128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'adds a missing subsetting element at the given index' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xms', value: '128m', insert_type: :index, insert_value: '1')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.create validate_file(expected_content_six, tmpfile) end expected_content_seven = <<-EOS JAVA_ARGS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'removes an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx')) provider = described_class.new(resource) expect(provider.exists?).to eq '192m' provider.destroy validate_file(expected_content_seven, tmpfile) end expected_content_eight = <<-EOS JAVA_ARGS="-Xmx192m" EOS it 'is able to remove several subsettings with the same name' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX')) provider = described_class.new(resource) expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' provider.destroy validate_file(expected_content_eight, tmpfile) end expected_content_nine = <<-EOS JAVA_ARGS="-Xmx256m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/pe-puppetdb/puppetdb-oom.hprof" EOS it 'modifies an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-Xmx', value: '256m')) provider = described_class.new(resource) expect(provider.exists?).to eq '192m' provider.value = '256m' validate_file(expected_content_nine, tmpfile) end expected_content_ten = <<-EOS JAVA_ARGS="-Xmx192m -XXtest -XXtest" EOS it 'is able to modify several subsettings with the same name' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: '-XX', value: 'test')) provider = described_class.new(resource) expect(provider.exists?).to eq ':+HeapDumpOnOutOfMemoryError' provider.value = 'test' validate_file(expected_content_ten, tmpfile) end end context 'when working with subsettings in files with unquoted settings values' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'master', setting: 'reports', } end let(:orig_content) do <<-EOS [master] reports = http,foo EOS end expected_content_one = <<-EOS [master] reports = foo EOS it 'removes an existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'http', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to eq '' provider.destroy validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [master] reports = http,foo,puppetdb EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'puppetdb', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '' validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [master] reports = http,foo somenewsetting = puppetdb EOS it "adds a new subsetting when the 'parent' setting does not already exist" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'somenewsetting', subsetting: 'puppetdb', subsetting_separator: ',')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '' validate_file(expected_content_three, tmpfile) end end context 'when working with subsettings in files with use_exact_match' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'master', setting: 'reports', use_exact_match: true, } end let(:orig_content) do <<-EOS [master] reports = http,foo EOS end expected_content_one = <<-EOS [master] reports = http,foo,fo EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) provider = described_class.new(resource) provider.value = '' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [master] reports = http,foo EOS it 'does not remove substring subsettings' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'fo', subsetting_separator: ',')) provider = described_class.new(resource) provider.value = '' provider.destroy validate_file(expected_content_two, tmpfile) end end context 'when working with subsettings in files with subsetting_key_val_separator' do let(:common_params) do { title: 'ini_setting_ensure_present_test', path: tmpfile, section: 'master', setting: 'reports', subsetting_separator: ',', subsetting_key_val_separator: ':', } end let(:orig_content) do <<-EOS [master] reports = a:1,b:2 EOS end expected_content_one = <<-EOS [master] reports = a:1,b:2,c:3 EOS it "adds a new subsetting when the 'parent' setting already exists" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3')) provider = described_class.new(resource) provider.value = '3' validate_file(expected_content_one, tmpfile) end expected_content_two = <<-EOS [master] reports = a:1,b:2 somenewsetting = c:3 EOS it "adds a new subsetting when the 'parent' setting does not already exist" do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'c', value: '3', setting: 'somenewsetting')) provider = described_class.new(resource) expect(provider.exists?).to be_nil provider.value = '3' validate_file(expected_content_two, tmpfile) end expected_content_three = <<-EOS [master] reports = a:1 EOS it 'is able to remove the existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b')) provider = described_class.new(resource) expect(provider.exists?).to eq '2' provider.destroy validate_file(expected_content_three, tmpfile) end expected_content_four = <<-EOS [master] reports = a:1,b:5 EOS it 'is able to modify the existing subsetting' do resource = Puppet::Type::Ini_subsetting.new(common_params.merge(subsetting: 'b', value: '5')) provider = described_class.new(resource) expect(provider.exists?).to eq '2' provider.value = '5' validate_file(expected_content_four, tmpfile) end end + + context 'when delete_if_empty is toggled to true' do + let(:common_params) do + { + title: 'ini_setting_delete_if_empty_test', + path: tmpfile, + section: 'master', + delete_if_empty: true, + } + end + + let(:orig_content) do + <<-EOS + [master] + reports = http + something = else + EOS + end + + expected_content_one = <<-EOS + [master] + something = else + EOS + + expected_content_two = '' + + it 'removes the subsetting when the it is empty' do + resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'reports', subsetting: 'http', subsetting_separator: ',')) + provider = described_class.new(resource) + provider.destroy + validate_file(expected_content_one, tmpfile) + resource = Puppet::Type::Ini_subsetting.new(common_params.merge(setting: 'something', subsetting: 'else', subsetting_separator: ',')) + provider = described_class.new(resource) + provider.destroy + validate_file(expected_content_two, tmpfile) + end + end end