inline_template newlines issue with ruby dsl
|Affected Puppet version:||Branch:|
When using an inline template, it does not like multiline statements.
The following example will throw an erb error when the master tries to compile it.
node “default” do
temp = "<% scope.to_hash.keys.each do |k| %>\n<%= k %>\n<% end %>" file '/tmp/test.txt', :content => inline_template(temp)
If there are no newlines it will compile without issue.
#3 Updated by Daniel Pittman over 1 year ago
- Category set to language
- Status changed from Unreviewed to Accepted
- Assignee set to Randall Hansen
- Keywords set to ruby dsl
Matthew Black wrote:
I figured out a work around, which might be the solution instead of a bug, which is to do this
:content => inline_template([temp])
It is different behavior than the Puppet DSL counter part.
Actually, that is identical behaviour to the Puppet DSL counterpart – it just looks different, because the Puppet DSL transparently wraps any argument in an array.
Thanks to the wonders of Ruby, though, there are some extra magic bits that make this extra-specially confusing:
The parser function is declared with a block taking a single argument,
vals. With Ruby 1.9.2 or later this result in an
inline_template function that accepts one and only one argument. With the Ruby 1.8 series, if you define a method from a block, and that block takes a single argument, Ruby transparently makes that an implicit
*vals argument – it collects multiple arguments and turns them into an array, but only if you have more than one.
That means we have exciting DSL behaviour differences in that
inline_template(part1, part2) will actually work as expected for a template with embedded newlines in Ruby 1.8, but it won’t with a single argument, because…
…the implementation uses
vals.collect to iterate over the array of templates supplied, compiling each one, and then concatenates the result at the far end.
For the Puppet DSL use case this means that you get one or more strings in an array, and everything works as expected.
When the Ruby DSL invokes the method “incorrectly” – in the way that is natural from Ruby, but not actually what Puppet expects – you get the effect of calling String#collect, not Array#collect, which is implicitly
String.split("\n").collect, and so tries to process every line of your input string as an independent template.
Fun fact, if you had newlines, but didn’t have any constructs that split template stuff over lines, it would work just fine. So, not only is this an awesome way to mess with people, it is a very difficult problem to build a mental model about the failure states of if you don’t read the implementation and know a bunch about how Ruby handles these objects.
This can be partly fixed by changing the method to work nicely from both the Ruby and Puppet DSL, but we should probably change the entire function calling prototype model that we use so that this doesn’t hit, eg, user defined parser functions in the same way.