diff --git a/lib/puppet/type/ini_setting.rb b/lib/puppet/type/ini_setting.rb index 4d1dfb9..c27406b 100644 --- a/lib/puppet/type/ini_setting.rb +++ b/lib/puppet/type/ini_setting.rb @@ -1,157 +1,157 @@ # frozen_string_literal: true require 'digest/md5' require 'puppet/parameter/boolean' Puppet::Type.newtype(:ini_setting) do desc 'ini_settings is used to manage a single setting in an INI file' ensurable do desc 'Ensurable method handles modeling creation. It creates an ensure property' newvalue(:present) do provider.create end newvalue(:absent) do provider.destroy end def insync?(current) if @resource[:refreshonly] true else current == should end end 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.' munge do |value| if value.match?(%r{(^\s|\s$)}) Puppet.warn('Settings should not have spaces in the value, we are going to strip the whitespace') end value.strip end end newparam(:force_new_section_creation, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Create setting only if the section exists' defaultto(true) 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 newproperty(:value) do desc 'The value of the setting to be defined.' munge do |value| if ([true, false].include? value) || value.is_a?(Numeric) value.to_s else value.strip.to_s end end 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') + def is_to_s(value) # rubocop:disable Naming/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 def insync?(current) if @resource[:refreshonly] true else current == should end end end newparam(:section_prefix) do desc 'The prefix to the section name\'s header.' defaultto('[') end newparam(:section_suffix) do desc 'The suffix to the section name\'s header.' defaultto(']') end newparam(:indent_char) do desc 'The character to indent new settings with.' defaultto(' ') end newparam(:indent_width) do desc 'The number of indent_chars to use to indent a new setting.' end newparam(:refreshonly, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'A flag indicating whether or not the ini_setting should be updated only when called as part of a refresh event' defaultto false end def refresh if self[:ensure] == :absent && self[:refreshonly] return provider.destroy end # update the value in the provider, which will save the value to the ini file provider.value = self[:value] if self[:refreshonly] end autorequire(:file) do Pathname.new(self[:path]).parent.to_s end end diff --git a/lib/puppet/type/ini_subsetting.rb b/lib/puppet/type/ini_subsetting.rb index a2e10d4..403e047 100644 --- a/lib/puppet/type/ini_subsetting.rb +++ b/lib/puppet/type/ini_subsetting.rb @@ -1,134 +1,134 @@ # frozen_string_literal: true 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.match?(%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') + def is_to_s(value) # rubocop:disable Naming/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 374f60d..aec0b7a 100644 --- a/spec/acceptance/ini_subsetting_spec.rb +++ b/spec/acceptance/ini_subsetting_spec.rb @@ -1,290 +1,290 @@ # frozen_string_literal: true 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 + 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 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 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 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