Using templates from another module

845 views
Skip to first unread message

Josh

unread,
May 3, 2012, 7:27:54 AM5/3/12
to puppet...@googlegroups.com
I have had a look around and cannot seem to find out whether this is possible. To explain further (using 2.7.13 btw)...

I have 
  • a module that I am using to configure various things about the network, lets call it 'my_network'
  • another module that defines my platform, for arguments sake 'my_webserver'
Now, in the 'my_network' module I have a definedtype that generates /etc/network/interfaces from a given ERB template which is associated with the platform, simplified version:

define my_network::interfaces ($template = $title) {
  file { '/etc/network/interfaces':
    ensure => present,
    content => template($template),
  }
}

I call this like so from 'my_webserver':

my_network::interfaces { my_webserver/interfaces_template.erb: }

This results in an error where it cannot see the template:

err: Could not retrieve catalog from remote server: Error 400 on SERVER: Failed to parse template my_webserver/interfaces_template.erb: undefined method `[]' for nil:NilClass at /local/puppet/env/production/modules/my_network/manifests/interfaces.pp:47 on node mynode.com

I also tried this using Classes rather then definedtypes and had the same problem.

What I want to do is have the function for handling network configuration in its own module (to avoid duplication of code and confusion) and the templates situated in another module specific to my node or platform.

So is there any way that I can use a template from another module? From my understanding of how puppet compiles the catalogue I cannot see why it wouldn't work?

Thanks
Josh

Josh

unread,
May 3, 2012, 10:13:48 AM5/3/12
to puppet...@googlegroups.com
In fact, after messing about a lot more this seems like a terrible idea since it removes the 'module as a stand-alone entity' concept and introduces a whole load of complexity.

This leaves me with global templates or just including all my templates in the my_network module ... think I'll go with the latter for overall consistency.

Josh

Nan Liu

unread,
May 3, 2012, 10:19:18 AM5/3/12
to puppet...@googlegroups.com
On Thu, May 3, 2012 at 4:27 AM, Josh <jo...@chickenmonkey.co.uk> wrote:
> I have had a look around and cannot seem to find out whether this is
> possible. To explain further (using 2.7.13 btw)...
>
> I have
>
> a module that I am using to configure various things about the network, lets
> call it 'my_network'
> another module that defines my platform, for arguments sake 'my_webserver'
>
> Now, in the 'my_network' module I have a definedtype that generates
> /etc/network/interfaces from a given ERB template which is associated with
> the platform, simplified version:
>
> define my_network::interfaces ($template = $title) {
>   file { '/etc/network/interfaces':
>     ensure => present,
>     content => template($template),
>   }
> }
>
> I call this like so from 'my_webserver':
>
> my_network::interfaces { my_webserver/interfaces_template.erb: }
>
> This results in an error where it cannot see the template:
>
> err: Could not retrieve catalog from remote server: Error 400 on SERVER:
> Failed to parse template my_webserver/interfaces_template.erb: undefined
> method `[]' for nil:NilClass at
> /local/puppet/env/production/modules/my_network/manifests/interfaces.pp:47
> on node mynode.com

Seems like puppet is invoking the correct template. The problem is
puppet failed to process the template.

A quick example with inline template:
$x = inline_template("<% var=nil %><%= var.[] %>")

Failed to parse inline template: undefined method `[]' for
nil:NilClass at /tmp/template.pp:1 on node raiden.home.lan

> I also tried this using Classes rather then definedtypes and had the same
> problem.
>
> What I want to do is have the function for handling network configuration in
> its own module (to avoid duplication of code and confusion) and the
> templates situated in another module specific to my node or platform.
>
> So is there any way that I can use a template from another module? From my
> understanding of how puppet compiles the catalogue I cannot see why it
> wouldn't work?

So in this case the template function tells you the error, but not the
line number, however you can get that info from the exception back
trace, so we can add that via Puppet.debug to
puppet/lib/puppet/parser/functions/template.rb:

rescue => detail
Puppet.debug(detail.backtrace.first)
raise Puppet::ParseError,
"Failed to parse template #{file}: #{detail}"
end

Now when we run it the template puppet debug gives the file
/tmp/example.erb and line number 10:

debug: /tmp/example.erb:10:in `result'
Failed to parse template /tmp/example.erb: undefined method `[]' for
nil:NilClass at /Users/nan/proj/tmp/invoke.pp:1 on node
raiden.home.lan

Maybe the file and line number should part of the error in the first
place. I've opened a ticket to sort out the best way we can improve
these things since it makes troubleshooting much easier:

http://projects.puppetlabs.com/issues/14296

Thanks,

Nan

jcbollinger

unread,
May 4, 2012, 8:41:23 AM5/4/12
to Puppet Users


On May 3, 9:13 am, Josh <j...@chickenmonkey.co.uk> wrote:
> In fact, after messing about a lot more this seems like a terrible idea
> since it removes the 'module as a stand-alone entity' concept and
> introduces a whole load of complexity.


I agree.


> This leaves me with global templates or just including all my templates in
> the my_network module ... think I'll go with the latter for overall
> consistency.


I'm missing something here: why do you not want to put the template
into the module that uses it?


John

Josh

unread,
May 4, 2012, 8:52:50 AM5/4/12
to puppet...@googlegroups.com
I'm missing something here: why do you not want to put the template
into the module that uses it?

Basically, we are an ISP and have a whole bunch of different platforms with different network configurations, that also differ depending which datacentre they are in.

I was hoping to have all the configuration for 'my_webserver', for example, in the class that defines that platform rather than have 100+ templates in the 'my_network' module (which then needs to be updated every time a new platform, or new network config for an existing platform, is added)

Fixed the template btw Nan, the problem was that I was calling data from a hash that didn't exist:

<%= hash['key1']['key2] %>

This results in the 'undefined method `[]' for nil:NilClass' error ... which while correct is pretty confusing. Think it is all part of the fact that hashes are new in Puppet and (as yet) not catered for particularly well. Cheers for the debug method, will come in handy!

Josh

jcbollinger

unread,
May 4, 2012, 8:53:24 AM5/4/12
to Puppet Users


On May 3, 6:27 am, Josh <j...@chickenmonkey.co.uk> wrote:
> Now, in the 'my_network' module I have a definedtype that generates
> /etc/network/interfaces from a given ERB template which is associated with
> the platform, simplified version:
>
> define my_network::interfaces ($template = $title) {
>   file { '/etc/network/interfaces':
>     ensure => present,
>     content => template($template),
>   }
>
> }


I don't like that at all. Generally speaking, defined types should
not manage resources whose identity is independent of the type
parameters. In your case, that implicates File['/etc/network/
interfaces']. If a defined type manages such a resource then at most
one instance can be declared, and for that you're usually better off
with a class.

I don't particularly care for parameterized classes, but in this case
your design already suffers from all the problems attending them.
Switching the definition to a parameterized class would at least have
the benefit of modeling the desired configuration more precisely.

Ideally, though, you would switch to an *un*parameterized class, and
communicate the erstwhile parameters to it via external data (e.g.
Hiera) and/or by creating separate [sub-]classes.


John

Josh

unread,
May 4, 2012, 9:46:59 AM5/4/12
to puppet...@googlegroups.com
I don't like that at all.  Generally speaking, defined types should
not manage resources whose identity is independent of the type
parameters. 

Yeah, I already binned that lot off and just used a class instead, as I say I was messing about to try and get it working
 
Ideally, though, you would switch to an *un*parameterized class, and
communicate the erstwhile parameters to it via external data (e.g.
Hiera) and/or by creating separate [sub-]classes.

The information from the host (ip address etc) is coming from hiera already so I am doing this already (maybe? unless I read that wrong). It is just the template that I cannot pull in via hiera (or can I ... can you just use multi-line yaml formatting and get the template from there? Will give it a whirl)

Josh

Josh

unread,
May 4, 2012, 9:52:17 AM5/4/12
to puppet...@googlegroups.com
(or can I ... can you just use multi-line yaml formatting and get the template from there? Will give it a whirl)

...of course not, what was I thinking :( 

jcbollinger

unread,
May 7, 2012, 10:10:22 AM5/7/12
to Puppet Users
Surprising. Did you use inline_template() to (try to) process the
template content? The template() function won't work, of course,
since it expects a file name, but I don't see why inline_template()
shouldn't work.


John
Reply all
Reply to author
Forward
0 new messages