Server Variables in Puppet Templates
filed in Linux on Feb.28, 2010
Puppet is an invaluable tool for managing a large number of Linux servers. By defining different classes for each service I deploy, I can easily define what runs on each server I control just by changing the site manifest.
A problem I ran into early when I was bringing services into Puppet was slightly different configurations on servers with different specs. For example, I run Tomcat on three servers but one is also running some other services. On this one server the JVM maximum heap size needs to be lower than on the others, but the rest of the Tomcat configuration is the same. To manage this without making a second class definition I used the template system in Puppet.
There are four steps to making this work. First you need to define a default for the variable. Next you need to write the template. Third, connect the template to a file on the client. Finally override the variable where you need it.
Define a default
The default variable definition goes in the module’s manifest file before the class definition. Putting it before the class definition makes sure that it is in scope for overriding in the node definition. The top of the manifest file will look like this:
$heapsize = 1000
class jvm {
...
Note that variables have leading dollar signs.
Write the template
Puppet uses Ruby’s ERB templating system. The template language is powerful, but I’m only going to cover variable substitution here.
Start with putting the configuration file in the templates subdirectory of the module, i.e. <module name>/templates/<config> so that Puppet knows where to find it. To substitute the variables, use <%= name %>. Here the variable name does not start with a dollar sign. Continuing the earlier example, this is part of our template:
export JAVA_OPTS="-Xmx<%= heapsize %>M"
Connecting the template to a file
Using the template in a file declaration is not as simple as just changing the path from the static source. Instead, you have to use the content parameter and the template function. The parameter to the template function is a weird path fragment: the first segment is the module name, and the rest is the path to your file underneath the templates directory. Continuing our example, assuming that the module is named jvm and the template is at modules/jvm/templates/options.erb, this declaration is in the jvm class:
file {"/usr/local/bin/jvm_options.sh":
mode => "664",
content => template("jvm/options.erb"),
}
Notice that the name of the template file doesn’t have to match the target name on the client.
Override the variable
Finally, you have to add a line to the node for the server which needs a different configuration. In the node definition before including the class, redefine the variable to what you need. Example:
node "lowmem" {
$heapsize = 750
include jvm
}
Nodes that don’t override the variable will get the default value defined in the module’s manifest file.
Puppet’s templates can do a lot more than simple variable substitution, but this should get you started. Beyond this it’s mostly ERB.
Dec 29th, 2010 on 9:12 am
Hey, it looks like you forgot to substitute the $heapsize variable in the template? I can’t see it used in the JAVA_OPTS definition.
Dec 29th, 2010 on 11:22 pm
You’re right. I introduced a formatting error the last time I edited the post and WordPress deleted it. Thanks for catching it!
May 12th, 2011 on 11:36 am
Hi
If I declare the default variable before the class definition, i get an error:
“Failed to parse template template/conf.erb: Could not find value for ‘variable'”
If I declare the variable inside the class then I dont get any errors, but can not override the variable from within the node definition.
Any idea where I might be going wrong?
Thanks
May 17th, 2011 on 8:23 pm
It’s possible that this doesn’t work with newer versions of puppet. I’ve moved to a different company since writing this, and I don’t use puppet anymore. Sorry I can’t help more, but let me know if you figure out the answer.
Apr 11th, 2012 on 12:42 pm
Regarding the scope of the $heapsize variable, another way of doing it is to use a parameterized class:-
class jvm($heapsize => 100) {
…
}
The node definition could then be as follows:-
node “lowmem” {
class { ‘jvm’: heapsize => 750 }
}
I think this is the better approach because the variable is scoped appropriately.