## Embedded Puppet Templates (EPP) Steve Traylen, IT-CM-LCS steve.traylen@cern.ch 16th November 2018 --- ## Typical Use Case ```shell # /etc/ntp.conf # List of time servers. server ip-time-0.cern.ch server ip-time-1.cern.ch ``` * Controlling a file with dynamic content. * Templates can **never** update existing files. --- ### Embedded Ruby (ERB) Example ```puppet # puppet manifest $servers = ['ip-time-0.cern.ch','ip-time-1.cern.ch' ] file{'/etc/ntp.conf': content => template('ntp/ntp.conf.erb') } ``` ```erb <%# erb template `ntp.conf.erb` %> # /etc/ntp.conf <% @servers.each do | node | -%> server <%= node %> <% end -%> ``` * Variables loaded from class's scope as ruby instance variable. --- ### Drawbacks to ERB templates * Learning ruby? CERN chose puppet rather than CHEF partly to avoid everyone learning ruby. * The various methods to access global scope have changed over the years: * `scope['ntp::server']` * `scope.lookupvar['ntp::server']` * Ruby allows more than should be done in templates, e.g. dns lookup, maths, ... --- ### Draw backs to ERB templates * Ruby can behave differently to puppet, e.g undefined variables: ```puppet $puppet_string = "${notset}abc" $erb_string = inline_template('<%= @notset %>'abc) ``` * `$puppet_string` will be `"abc"`. * `$erb_string` results in `@notset` is undefined. * Created complicated conditions in templates: ```erb <% if ! @notset.nil? || @notset == '' %-> notset is <@= @noset %> <% end -%> ``` --- ### EPP templates * Embedded Puppet templates added with Puppet 4. * Syntax very familiar to puppet e.g `$variable` * Two functions exist: `epp` and `epp_inline` * Note ERB is not currently going anywhere. --- ### EPP templates behave like defined types * Variables can be passed in as parameters from manifest. * Variables can be accessed from global scope with full path. * With `epp` function local variables can not be accessed. * With `epp_inline` local manifest variables can be accessed. --- ### Embedded Puppet(epp) Example ```puppet class ntp{ $servers = ['ip-time-0.cern.ch','ip-time-1.cern.ch' ] file{'/etc/ntp.conf': content => epp('ntp/ntp.conf.epp',{ servers => $servers}) } } ``` ```epp <%# epp template ntp.conf.epp %> # /etc/ntp.conf <% $servers.each | $_node | { -%> server <%= $_node %> <% } -%> ``` --- * <% %> execute puppet DSL. * <%= %> execute puppet DSL and print output. * A `-` can be used to suppress white space and a newline. ```epp <%# A epp comment %> <% ['a','b','c'].each |$_i| { -%> <%- ['x','y','z'].each |$_j| { -%> <%= $i %> <%= upcase($j) %> <-% } -%> <%- } -%> ``` ``` a X a Y a Z b X ... ``` --- ## Type Validation ```epp <%- | Array[Stdlib::Fqdn] $servers, | -%> # /etc/ntp.conf <% $servers.each | $_node | { -%> server <%= $_node %> <% } -%> ``` * Type definitions at start of file. * Defaults can be specified. * Following would fail as invalid hostname. ```puppet $content => epp('ntp/ntp.conf.epp',{ servers => ['ntp_0.cern.ch'], }) ``` --- ## Global Variables Space ```puppet class ntp{ $servers = ['ip-time-0.cern.ch','ip-time-1.cern.ch' ] file{'/etc/ntp.conf': content => epp('ntp/ntp.conf.epp') } } ``` ```epp # /etc/ntp.conf <% $ntp::servers.each | $_node | { -%> server <%= $_node %> <% } -%> ``` * Accessed same as puppet, full path. * The facts hash `$facts['os']['family']`. --- ## Inline EPP ```puppet $var = 'value' $erb_string = inline_template('<%= @notset %>'abc) $epp_string = inline_epp('<%= $notset %><%= $var %>abc') ``` * `inline_epp` much like erb's `inline_template`. * Local variables are loaded locally, unlike `epp`. * Unset variables will evaluate to "" like puppet. * ERB was throwing an error. --- ## Command Line Render ```bash $ puppet epp render file.epp \ --values \ '{"servers" => ["ntp1.example.org","ntp2.example.org"]}' server ntp1.example.org server ntp2.example.org ``` * EPP can be redered on the command line. * Buggy with puppet 4. Okay with puppet 5. --- ### HEREDOC Simple ```puppet $motd = @(EOF) Welcome to this machine It is best machine at CERN. EOF ``` * Puppet manifests support HERE docs. --- ### HEREDOC Left Justification ```puppet if $setmotd { $motd = @(EOF) This machine is even better though. This text is actually left justified | EOF } ``` * Left justification can be offset. * This make indented code a lot prettier. * Lines up with `|` symbol. --- ### HEREDOC Substitution ```puppet $motd = @("EOF") This machine $::fqdn is really the best machine. | EOF ``` * Variables can be substituted. * Quotes around the `EOF`. --- ### File Validation ```puppet file{'/etc/sshd/sshd_config': content => epp('ssh/sshd_config.epp'), validate_cmd => '/usr/sbin/sshd -t %' } ``` ``` /usr/sbin/sshd -t /opt/pl/cache/sshd_config ``` * Executes a command to check validity of file * Non valid file: * actual destination not replaced. * resource fail. * no service restart. --- ### Resources * [Puppet EPP Reference Guide](https://puppet.com/docs/puppet/5.5/lang_template_epp.html) * [Puppet HEREDOC Guide](https://puppet.com/docs/puppet/5.4/lang_data_string.html#heredocs) * These slides: * https://codimd.web.cern.ch/p/S1pIkDtaX#/ --- ### Conclusions of EPP * There are no downsides to using EPP templates. * Maybe lack of syntax highlighting in vim, gitlab, ... * The type validation is the most useful feature: * I broke a service yesterday with 12GB vs 12G. * After converting to EPP that will never happen again.
{"title":"Puppet Embedded Templates(EPP)","tags":"puppet epp erb templates","progress":true,"slideOptions":{"center":false,"theme":"moon","transition":"fade"}}