Search code examples
rubychef-infrachef-custom-resource

Chef Custom Resources : mutable property or an alternative


I read that as of 2016 OpsCode recommends against LWRP and the alternative HWRP. They rather recommend to use custom resources.

While this makes sense it leaves a lot to be desired in terms of leveraging ruby (unless I've misunderstood?)

I would like to be able to modify an array based on some boolean properties. I can then use that array to pass to the template, as in:

property :get_iptables_info, [TrueClass, FalseClass], default: false
property :get_pkglist, [TrueClass, FalseClass], default: false
property :cw_logs, Array, default: [], required: false

action :create do

  ruby_block 'cw_iptables' do
    block do
      new_resource.cw_logs.push({ "#{new_resource.custom_dir}/iptables/iptables.txt" => { "log_group_name" => new_resource.log_group_name+"/iptables"}})
    end
    action :run
    only_if {new_resource.get_iptables_info}
  end

  template "my_template" do
    variables ({:logstreams => cw_logs})
  end

end

Then in my template:

<% @logstreams.each_pair do |path, _object| %>
["#{path}"]
log_group_name = _object["log_group_name"]
<% end %>

The problem is that properties are immutable. So I get the error:

RuntimeError
------------
ruby_block[cw_iptables] (/tmp/kitchen/cache/cookbooks/custom_cw/resources/logs.rb line 43) had an error: RuntimeError: can't modify frozen Array

What's the correct way to do this? What's the correct way to write ruby code within the resource so that it's more modular and uses methods/functions?


Solution

  • Mutating properties inside a custom resource is generally a bad idea (there are exceptions but this isn't one of them). Better would be to use a local variable. Related, you don't need to use a ruby_block like that when you're already in a custom resource's action:

    action :create do
      cw_logs = new_resource.cw_logs
      if new_resource.get_iptables_info
        cw_logs += [whatever extra stuff you want]
      end
    
      template "my_template" do
        variables logstreams: cw_logs
      end
    end
    

    Notably that's using += instead of push which doesn't mutate the original. If you used push you would want cw_logs = new_resource.cw_logs.dup or similar.