diff --git a/README.markdown b/README.markdown index 2583dbb..5f082af 100644 --- a/README.markdown +++ b/README.markdown @@ -95,10 +95,10 @@ hocon_setting { 'sample map setting': * `value`: The value of the HOCON file setting to be defined. * `type`: The type of the value passed into the `value` parameter. This value should be a string, with valid values being - `'number'`, `'boolean'`, `'string'`, `'hash'`, `'array'`, and `'text'`. + `'number'`, `'boolean'`, `'string'`, `'hash'`, `'array'`, `'array_element'`, and `'text'`. This parameter will not be need to be set most of the time, as the module - is generally smart enough to figure this out on its own. There are only two cases in which this parameter is required. + is generally smart enough to figure this out on its own. There are only three cases in which this parameter is required. The first is the case in which the `value` type is a single-element array. In that case, the `type` parameter will need to be set to `'array'`. So, for example, to add a single-element array, you would add the following to your manifest @@ -112,7 +112,22 @@ hocon_setting { 'sample map setting': type => 'array', } ``` - + + If you are trying to manage single entries in an array (for example, adding to an array from a define) you will need tot set the `'type'` parameter to + `'array_element'`. For example, to add to an existing array in the 'foo' setting, you can add the following to your manifest + + ``` + hocon_setting { 'add to array': + ensure => present, + path => '/tmp/foo.conf', + setting => 'foo', + value => 2, + type => 'array_element', + } + ``` + + Note: When adding an item via 'array_element', the array must already exist in the HOCON file. + Since this type represents a setting in a configuration file, you can pass a string containing the exact text of the value as you want it to appear in the file (this is useful, for example, if you want to set a parameter to a map or an array but want comments or specific indentation on elements in the map/array). In this case, `value` must be a string with no leading or trailing whitespace, newlines, or comments that contains a valid HOCON value, and the @@ -165,7 +180,7 @@ hocon_setting { 'sample map setting': } ``` - Aside from these two cases, the `type` parameter does not need to be set. + Aside from these three cases, the `type` parameter does not need to be set. ##Development diff --git a/lib/puppet/provider/hocon_setting/ruby.rb b/lib/puppet/provider/hocon_setting/ruby.rb index 872820d..b4004c6 100644 --- a/lib/puppet/provider/hocon_setting/ruby.rb +++ b/lib/puppet/provider/hocon_setting/ruby.rb @@ -11,7 +11,18 @@ def self.namevar(section_name, setting) end def exists? - conf_file.has_value?(setting) + # we only want to check for the individual element for idempotence when + # absent, otherwise the new value will be picked up by insync? + if resource[:type] == 'array_element' and resource[:ensure] == :absent + Array(@resource[:value]).each do |v| + if value.flatten.include?(v) + return true + end + end + return false + else + conf_file.has_value?(setting) + end end def create @@ -21,7 +32,7 @@ def create end def destroy - conf_file_modified = conf_file.remove_value(setting) + conf_file_modified = remove_value(resource[:value]) write_conf(conf_file_modified) @conf_file = nil end @@ -82,8 +93,32 @@ def conf_object Hocon::ConfigFactory.parse_file(file_path) end + def remove_value(value_to_remove) + if resource[:type] == 'array_element' + new_value_tmp = [] + val = value + new_value_tmp << val + new_value_tmp.flatten! + Array(value_to_remove).each do |v| + new_value_tmp.delete(v) + end + new_value = Hocon::ConfigValueFactory.from_any_ref(new_value_tmp, nil) + conf_file_modified = conf_file.set_config_value(setting, new_value) + else + conf_file_modified = conf_file.remove_value(setting) + end + conf_file_modified + end + def set_value(value_to_set) - if resource[:type] == 'array' || (value_to_set.is_a?(String) && resource[:type] != 'text') || (value_to_set.is_a?(Array) && value_to_set.size > 1) + if resource[:type] == 'array_element' + tmp_val = [] + tmp_val << value + tmp_val << value_to_set + tmp_val.flatten! + tmp_val.uniq! + new_value = Hocon::ConfigValueFactory.from_any_ref(tmp_val, nil) + elsif resource[:type] == 'array' || (value_to_set.is_a?(String) && resource[:type] != 'text') || (value_to_set.is_a?(Array) && value_to_set.size > 1) new_value = Hocon::ConfigValueFactory.from_any_ref(value_to_set, nil) elsif resource[:type] == 'text' new_value = value_to_set[0] diff --git a/lib/puppet/type/hocon_setting.rb b/lib/puppet/type/hocon_setting.rb index 3c57c93..f68781a 100644 --- a/lib/puppet/type/hocon_setting.rb +++ b/lib/puppet/type/hocon_setting.rb @@ -61,7 +61,7 @@ unless value.is_a?(Hash) raise "Type specified as 'hash' but was #{value.class}" end - when nil + when 'array_element', nil # Do nothing, we'll figure it out on our own else raise "Type was specified as #{@resource[:type]}, but should have been one of 'boolean', 'string', 'text', 'number', 'array', or 'hash'" @@ -75,6 +75,33 @@ end value end + + def insync?(is) + if @resource[:type] == 'array_element' + # make sure all passed values are in the file + Array(@resource[:value]).each do |v| + if not provider.value.flatten.include?(v) + return false + end + end + return true + else + super + end + end + + def change_to_s(current, new) + if @resource[:type] == 'array_element' + real_new = [] + real_new << current + real_new << new + real_new.flatten! + real_new.uniq! + "value changed [#{Array(current).flatten.join(", ")}] to [#{real_new.join(", ")}]" + else + super + end + end end validate do diff --git a/spec/unit/puppet/provider/conf_setting/ruby_spec.rb b/spec/unit/puppet/provider/conf_setting/ruby_spec.rb index 5feba24..4c13e58 100644 --- a/spec/unit/puppet/provider/conf_setting/ruby_spec.rb +++ b/spec/unit/puppet/provider/conf_setting/ruby_spec.rb @@ -28,6 +28,90 @@ def validate_file(expected_content,tmpfile = tmpfile) end end + context "array_element" do + let(:orig_content) { + <<-EOS +test_key_1: [ + { + foo: foovalue + bar: barvalue + master: true + } +, + { + foo: foovalue2 + baz: bazvalue + url: "http://192.168.1.1:8080" + } +, + { + foo: foovalue3 + } +] + EOS + } + + it "should add a new element to the array" do + resource = Puppet::Type::Hocon_setting.new(common_params.merge( + :setting => 'test_key_1', :value => [{ 'foo' => 'foovalue3' }, { 'bar' => 'barvalue3' }], :type => 'array_element')) + provider = described_class.new(resource) + expect(provider.exists?).to be true + provider.create + validate_file( + <<-EOS +test_key_1: [ + { + "bar" : "barvalue", + "foo" : "foovalue", + "master" : true + } +, + { + "baz" : "bazvalue", + "foo" : "foovalue2", + "url" : "http://192.168.1.1:8080" + } +, + { + "foo" : "foovalue3" + } +, + { + "bar" : "barvalue3" + } + +] + EOS +) + end + + it "should remove elements from the array" do + resource = Puppet::Type::Hocon_setting.new(common_params.merge( + :setting => 'test_key_1', :ensure => 'absent', :value => { 'foo' => 'foovalue3' }, :type => 'array_element')) + provider = described_class.new(resource) + expect(provider.exists?).to be true + provider.destroy + validate_file( + <<-EOS +test_key_1: [ + { + "bar" : "barvalue", + "foo" : "foovalue", + "master" : true + } +, + { + "baz" : "bazvalue", + "foo" : "foovalue2", + "url" : "http://192.168.1.1:8080" + } + +] + EOS +) + end + end + context "when ensuring that a setting is present" do let(:orig_content) { <<-EOS diff --git a/spec/unit/puppet/type/hocon_setting_spec.rb b/spec/unit/puppet/type/hocon_setting_spec.rb index 3b19f05..149b085 100644 --- a/spec/unit/puppet/type/hocon_setting_spec.rb +++ b/spec/unit/puppet/type/hocon_setting_spec.rb @@ -30,6 +30,7 @@ 'text', 'number', 'array', + 'array_element', 'hash' ]