Skip to content

Commit

Permalink
Add array_element
Browse files Browse the repository at this point in the history
This allows elements in arrays in hocon files to managed individually.
  • Loading branch information
Morgan Haskel committed Sep 10, 2015
1 parent 17bdcb8 commit 1f5ea7c
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 8 deletions.
23 changes: 19 additions & 4 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
41 changes: 38 additions & 3 deletions lib/puppet/provider/hocon_setting/ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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]
Expand Down
29 changes: 28 additions & 1 deletion lib/puppet/type/hocon_setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'"
Expand All @@ -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
Expand Down
84 changes: 84 additions & 0 deletions spec/unit/puppet/provider/conf_setting/ruby_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions spec/unit/puppet/type/hocon_setting_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'text',
'number',
'array',
'array_element',
'hash'
]

Expand Down

0 comments on commit 1f5ea7c

Please sign in to comment.