Chef: How to modify autogenerated file

chef

I have to modify autogenerated nginx config, based on some conditions:
Now in recipe i include template:

    template "#{node['nginx']['dir']}/sites-available/#{node['fqdn']}" do
            source 'nginx-site.erb'
            owner  'root'
            group  node['root_group']
            mode   '0600'
            notifies :reload, 'service[nginx]'
    end

And then in template i change by using regexp i change file content:

    <% node['nginx']['sites'].each do |site|
            if File::exist?(site['include_path'])
    %>
    <% if not node['nginx']['edperl'] %>
            <%= File::read(site['include_path']) %>
    <% else %>
    <%= File::read(site['include_path']).gsub(/(access_log.*?;)/, '\1' + "\n    set $pseclvl $seclvl;") %>
    <% end -%>
    <%    end
            end
    %>

`

Now i need to add 2 another if statements.
If i do this way, result file contain 3 identical site definition, each modified in different if statements.

What is the best way to work with existing file ?
I try without success ruby code in file template and found "line" cookbook. If i use line cookbook – how to use it in nginx cookbook recipe ?

Thank you for the answers.

So, i need to do this logic on autogenerated file:

    if node['nginx']['attribute1']
            add to a file line1 after access_log statement
    end
    if node['nginx']['attribute2']
         add to a file line2 after access_log statement
    end
    if node['nginx']['attribute3']
         add to a file line3 after access_log statement
    end

Best Answer

Chef is fairly opinionated about how to do this. You should manage the entire file in Chef, and put the logic in the template or in data passed into the template. For what it's worth, the 'Chef way' would be to manage the entire file and the files you're calling File::read on.

In your case, the logic is getting a bit elaborate, so I'd recommend calculating what you want ahead of time, e.g.

included_str = ''
node['nginx']['sites'].each do |site|
    next unless ::File::exist?(site['include_path'])

    if not node['nginx']['edperl']
        included_str << File::read(site['include_path'])
    else
        included_str << File::read(site['include_path']).gsub(/(access_log.*?;)/, '\1' + "\n    set $pseclvl $seclvl;") %>
    end

    included_str << "\n"
end

And then when you render your template, pass that in:

template "#{node['nginx']['dir']}/sites-available/#{node['fqdn']}" do
    source 'nginx-site.erb'
    owner  'root'
    group  node['root_group']
    mode   '0600'
    notifies :reload, 'service[nginx]'
    variables(included_sites: included_str)
end

And then in your template, just spit out that String:

<%= included_sites %>

If you don't manage the entire thing in Chef, you're also likely to run into order of operation issues, e.g. you're going to call File::read on a file copied by Chef, but because of Chef's compile-then-converge model, you'll be trying to read a file before the converge copies it.