Puppet hiera variable in erb `if` statement - puppet

I have a hiera variable in my erb template for one condition check.
<% if #server_name == 'jira-dev-api'
After upgrading to puppet 5, this variable in the if loop does not work. But this will take correct values without the if statement in an erb template.
I tried puppet lookup for hiera debugging, and it works fine. The variable is defined within the same module as below.
module_name::install_confluence_apache_config::server_name=xyz
When I appended a scope with the absolute path like below,
scope['module_name::install_confluence_apache_config::server_name']
it works.
Is there any change with erb scoping in if statement in puppet 5? I was using puppet 3 open source.
Adding more info.
a)server_name is a variable defined in "nodes/%{::clientcert}" yaml file.
b)I was using puppet 3.8 and the working erb template was as below
<% if #server_name == 'jira-dev-api'
c)upgraded to puppet 5.5.But observed that when i reference server_name variable in erb in a if statement as above the value is not getting.But working when i use
scope['module_name::install_confluence_apache_config::server_name']
Also this issue is only inside if statment in erb template.

Thanks John and alex.
It was a scope issue as John said it was working before in 3.8 using #syntax may be due to the bug.

Related

Puppet contain not working to order class inclusion

I am trying to force a Puppet class that creates a file to be processed before another class that needs this file to exist to run properly. Following the Puppet article Language: Containment of resources I am using contain.
My code does not work and I do not understand why. It gives this error:
Error: Evaluation Error: Error while evaluating a Function Call, Failed to parse template testing/def.erb:
Filepath: /root/local/testing/templates/def.erb
Line: 1
Detail: No such file or directory # rb_sysopen - /tmp/abc
at /root/local/test2.pp:16:16 on node example.com
Here is the code (stripped down):
### test2.pp
class klass1 {
file { '/tmp/abc':
content => 'xxx',
}
}
# Stage 0 creates the file /tmp/abc.
class stage0 {
contain klass1
}
# Stage 1 uses the contents of /tmp/abc to create the
# file /tmp/def.
class stage1 {
file { '/tmp/def':
content => template('testing/def.erb'),
}
}
# Try to force stage0 to be loaded before stage1.
include stage0
class { 'stage1':
require => Class['stage0']
}
### testing/templates/def.erb
Contents: <%= File.read("/tmp/abc") %>
I am using Puppet 5.3.3.
The issue here does not relate to containment, but to the dependency in your template at compile time on the call to File.read("/tmp/abc").
Ordinarily, compilation occurs on the Puppet Master a.k.a. Puppet Server, and the template function also runs at this time. Thus, your template def.erb attempts to read from a nonexistent file at compile time on the Puppet Master.
A better solution is likely to be define the content of file /tmp/abc in Puppet itself as data or a variable and then pass that variable to the template function, and so remove the dependency on reading from the file on disk altogether.
Without fully understanding why you were trying to separate this file content into multiple classes in the first place, I can't really comment any further.
Puppet is a declarative language that is typically used to ensure certain state of resources. But if you really need make a decision based on local code evaluation, you're basically left with 2 options:
Firstly, use facter, in some module create lib/facter/my_fact.rb:
#!/usr/bin/env ruby
require 'facter'
Facter.add(:load1) do
confine kernel: 'Linux'
setcode do
Facter::Util::Resolution.exec("cat /proc/loadavg | awk '{print $1}'")
end
end
Then your "function" result will be available via facter load1 (from shell) or $facts['load1'] from Puppet code. Such code is always evaluated before applying Puppet's catalog. If your function doesn't take arguments, this might be a good option. Note this is a silly example, the load is already available via facter load_averages.1m (though the usefulness of such fact is questionable). Using too many facts isn't good idea, it would prolong time required for applying a Puppet catalog. There's a soft limit for number of facts on puppetserver.
Second option would be using Deferred function. Evaluation of such function is delayed into later phase of catalog application (won't be evaluated on compile server).
Puppet code looks like this:
$value = Deferred("mymodule::load", ["1m"])
and the actual implementation should be in a Puppet module in a Ruby function, e.g. lib/puppet/functions/load.rb:
Puppet::Functions.create_function(:'mymodule::load') do
dispatch :load do
param 'String', :load_type
return_type 'String'
end
def load(load_type)
case load_type
when '1m'
avgs[0]
when '5m'
avgs[1]
else
raise "#{load_type} not supported"
end
end
end
The advantage is the latter approach is that you can pass multiple arguments. Although returning more complex types than String or Numeric doesn't seem to currently supported.

Puppet iteration from external file

I'm new to configuration management, just FYI.
I'm trying to puppetize elasticsearch, and want to have a master list of elasticsearch nodes in a file (which can be used for multiple things, not just this purpose).
I would like to add elasticsearch.yml via an ERB template and expand the list of FDQN's into the discovery.zen.ping.unicast.hosts: [] param.
For example I have an external file called es_hosts in module/files that contains:
host1.domain.com
host2.domain.com
host3.domain.com
host4.domain.com
Then when puppet builds the ERB template have this in the param:
discovery.zen.ping.unicast.hosts: ["host1.domain.com", "host2.domain.com", "host3.domain.com", "host4.domain.com"]
I've tried a few things, but I can't get my head wrapped around it.
I would be using this list for other things like building firewall rules, etc, so I'd like to have one master list for reference that can be updated by my team.
Thanks for any help!
Rather than have a list in a file, it would be better to have it in Hiera, since defining lists and other external data is specifically what Hiera is for.
(If you have not used Hiera yet, you definitely should read up on it.)
So in Hiera you would have:
---
es_hosts:
- host1.domain.com
- host2.domain.com
- host3.domain.com
- host4.domain.com
In your manifest, you would read that in from Hiera using the hiera function:
$es_hosts = hiera('es_hosts')
(Note that instead of the hiera function, we often use Puppet's Automatic Parameter Lookup feature instead to read data into our manifests from Hiera, but your requirement here - a list of ES hosts to be used in multiple contexts - suggests you will want this list not to be bound to a specific class input. If this does not make sense to you right now, you will need to learn about Parameterised Classes and Automatic Parameter Lookup, but it's otherwise not relevant to this answer.)
Finally, in your ERB template you would have:
discovery.zen.ping.unicast.hosts: ["<%= #es_hosts.join('", "') %>"]
Pay attention to the fact that the $es_hosts variable from your manifest is accessed via a Ruby instance variable #es_hosts in your ERB template.
Finally, note that there is an Elasticsearch Puppet module available on the Puppet Forget here. You may find that using that module is better than writing your own.

How do I include a file with a puppet manifest?

I have a very simple use for puppet; I create and delete students from a test server. I would like to store some configuration variables in another file and then just do a simple: include 'variables.pp' at the top of my manifest file. Is there a simple way to do this? I have consulted:
https://docs.puppet.com/puppet/latest/reference/lang_classes.html#using-include
https://puppet.com/blog/problem-separating-data-from-puppet-code
None of which give a simple solution. If I can only use the solutions above, I'll just hardcode it in my manifest.
Puppet 3 has an import statement that should serve your purpose. It is deprecated (and removed from Puppet 4), but it will do the job for you in Puppet 3:
variables.pp
# top-scope variables:
$var1 = 'foo'
$var2 = 'bar'
main.pp
import 'variables.pp'
# demo
notify { "var1 = ${var1}; var2 = ${var2}": }
Note that Puppet's import does not perform text interpolation; it is more like Python's import, making complete declarations from another manifest visible in the importing manifest.

What does (Pakyow::Presenter::MissingTemplate) mean?

.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/bundler/gems/pakyow-9d4d9be98b47/pakyow-presenter/lib/presenter/view_store.rb:105:in `template_with_name': No template named 'default' (Pakyow::Presenter::MissingTemplate)
Pakyow is looking for a template named default.html (in the app/views/_templates path unless you've changed your template path config option). If you're running on HEAD, this is likely being caused by a change to the default template name. In the last release, it was pakyow; now it's named default.

Use puppet database variables in manifest

I am attempting to learn puppet, and so far so good.
What I am having an issue with is using a variable that I have set for a node or group in the web console.
I created a variable called myCustomSetting, and then in a manifest:
file {/var/tmp/myfile.txt
ensure => file,
content => $::myCustomSetting,
}
When I apply the manifest with puppet apply mytest.pp, there are no errors, but the file is empty? What am I missing?
The double semi colon indicate that you wish to reach the top level scope, where you supposedly declared your variable. Check that your variable has not been declared in a local scope.

Resources