puppet, getting the $name when instantiating a resource type with an array - puppet

using puppet, i need to create three files, with this content:
/tmp/f1.txt: hello /tmp/f1.txt
/tmp/f2.txt: hello /tmp/f2.txt
/tmp/f3.txt: hello /tmp/f3.txt
i try as follows:
$path="/tmp/"
$my_files = ["$path/f1.txt", "$path/f2.txt", "$path/f3.txt"]
file { $my_files:
ensure => file,
content => "hello $name\n",
}
however this does not work because $name is undefined.
is there a variable that gets instantiated for each 'iteration' and that i can use?
ps: i am aware that i could create a new resource type as follows:
define file_with_content {
file { $name:
ensure => file,
content => "hello $name\n",
}
}
$path="/tmp/"
$my_files = ["$path/f1.txt", "$path/f2.txt", "$path/f3.txt"]
file_with_content { $my_files: }
but this requires creating a new resource type,
and I cannot do this in my context (which is not explained here).
the question is, how to modify the first code to make it work, without defining a new resource type, nor executing shell code?

You only can access the namevar for defined types. For Puppet's resources, the results are unpredictable - for example, $name for File will give you main, or the current stage. Additionally, you cannot pass/utilize extra parameters to Puppet's resources as they have their own set of parameters already.
The standard solution has been to wrap the File declaration in a defined type like here, like your first. Perhaps you can explain why that cannot be used, so some other solution could be devised?

Related

Puppet data array

I got a module that creating some directories depending of server:
class linux_sftp::sftp_mount ($sftp_mount_ip, $sftp_mount_username, $sftp_mount_password, $sftp_mount_point) {
file { "/mnt/${sftp_mount_point}":
ensure => directory,
subscribe => Exec['sftp_remount'],
}
in data.yml
sftp_mount_point: "stcontent1"
I want to add to data more folders like: stcontent2, stcontent3. Is it a way to add this and loop thru data?
sftp_mount_point:
- "stcontent1"
- "stcontent2" ...
Yes you can use lambda method (can also be invoked as functions if desired) iteration to accomplish this task. The most common for your use case is each. It can be easily invoked on type Array[String] like you have in your question.
$sftp_mount_point.each |String $mount| {
file { "/mnt/${mount}":
ensure => directory,
}
}
Note that the file type does not have a subscribable property, so subscribe is not a valid attribute and I therefore removed it above.

Iterate a puppet resource collector

I am trying to develop a puppet class with a defined resource which creates the configuration for a website.
One of the things that the defined resource has to do is assign the IP address of the website to a dummy interface. Due to constraints of the project this is done with NetworkManager.
So I have to generate a file like
[connection]
id=dummydsr
uuid=50819d31-8967-4321-aa34-383f4a658789
type=dummy
interface-name=dummydsr
permissions=
[ipv4]
method=manual
#IP Addresses come here
ipaddress1=1.2.3.4/32
ipaddress2=5.6.7.8/32
ipaddress3=8.7.6.5/32
[ipv6]
method=ignore
There is to be a line ipaddressX=... for every instance of the defined resource.
My problem is how do I track the number of times the defined resource has been instantiated so I can somehow increment a counter and generate the ipaddress lines.
Or for each instantiated defined resource, append the IP address to an array which I can later use to build the file
If I understand you, and I'm not certain that I do, but I think you would want to do something like this:
define mytype(
Integer $count,
...
) {
file { 'some_network_manager_file':
content => template(...)
}
}
And then you would have a loop:
$mystuff.each |$count, $data| {
mytype { ...:
count => $count,
...
}
}
Key insight here may be that the each function has some magic in it that allows you to get the index if you need it, see also this answer.
Now I think that's how it will work, without me spending time researching NetworkManager. If you provide more of your code, I may be able to update this to be more helpful.
This is less than ideal since I would prefer to have it inside the defined resource, but since I instantiate the defined resource with the data from a hash I use said hash to iterate that part.
class xxx_corp_webserver (
Hash $websites ={}
){
create_resources('xxx_corp_webserver::website', $websites)
# This would be nicer inside the defined class, but I did not find any other way
# Build and array with the IP addresses which are for DSR
$ipaddresses = $websites.map | $r | {
if $r[1]['enabledsr'] {
$r[1]['ipaddress']
}
}
# For each DSR address add the line
$ipaddresses.each | Integer $index , String $ipaddress | {
$num = $index+1
file_line{"dummydsr-ipaddress${num}":
ensure => present,
path => '/etc/NetworkManager/system-connections/dummydsr',
line => "address${num} = ${ipaddress}/32",
match => "^address.* = ${ipaddress}/32",
after => '# IP Addresses come here',
notify => Service['NetworkManager'],
require => File['/etc/NetworkManager/system-connections/dummydsr'],
}
}
}

Rspec Puppet: Defined Type iteration

Using Puppet 3
Testing using rspec-puppet
Iterating over an array of hashes using a Defined Type
Getting an Error, telling me that my parameter (which defaults to the value of $title) cannot be accessed the way I am because it is not an Array or Hash
I'm using old-style iteration in a puppet module, creating a defined type to iterate over an array of hashes. I'm trying to write a test for this define in rspec-puppet, attempting to assign a hash to the :title using let(). The $title is then supposed to be set to my variable called $daemon, yet my tests keep throwing errors saying that $daemon is not a hash or array.
Here's how I'm creating my defined type:
define my_module::daemon_install ($daemon = $title) {
package {"${daemon['package_name']}":
ensure => "${daemon['package_version']}",
}
file {"${some_fact}/${daemon['binary']}.conf":
ensure => file,
content => "blah"
notify => Service["${daemon['name']}"],
}
service {"${daemon['name']}":
ensure => running,
enable => true,
}
}
And here's how I'm trying to set the title:
describe 'my_module::daemon_install' do
context 'with foo' do
let(:title) {
{
"name" => "foo",
"package_name" => "bar",
"package_version" => "1.0.1",
"binary" => "food",
}
}
# ...
end
end
And here's the error:
daemon is not a hash or array when accessing it with package_version
I'm actually abit new to using defined types for iteration, and very new at rspec-puppet, so I'm not sure if I'm missing something obvious here or not.
But why is it only complaining about package_version and not package_name? And more importantly: why is it not a hash, when (I believe) I'm setting it to a hash correctly in the spec file.
I should mention that another test, of a class which uses this defined type, completes successfully. So it seems related to how I'm trying to set the title when directly testing the define, if I were to guess.
Rspec always converts title into String.
Use $name in define() instead of $title and add the following into tests:
let :title do
{ ... }
end
let :params do
{ :name => title }
end
Please note$name should be equal of $title.

When I use definition instead of class in puppet, what's the best practice for parameters?

I realise that it's generally a good idea to create params.pp in the module with modulename::params class and inherit that in modulename class to handle parameters in a separate file. How do I do that if instead of class, I am creating a definition?
Just to clarify, I'm using a definition to be able to install multiple versions of the same application on the server.
Good question. Since there is no inheritance available for defined types in Puppet the params.pp patterns can not be reproduced in the exact same way for defined types as for classes. There is another way though.
The following code outputs 'hello world' via the Foo['bar'] defined type:
class params {
$msg = 'hello world'
}
define foo($msg = $params::msg ) {
notify{ $msg: }
}
foo { 'bar': }
include params
Now, for the above to function it is necessary for params to be included. Otherwise the Puppet parser will complain that the class params has not been evaluated and therefore the $params::msg variable can not be resolved.
It is not necessary to provide ordering between the inclusion of params and the definition of bar, since in Puppet classes are always evaluated before defined types. If this would not be so the above would likely cause the same evaluation problem and you would have to write:
foo { 'bar':
require => Class['params'] # <- not necessary
}
include params
So for this to work in a module foo you can simply add a params class as you are used to and start your init.pp with:
include foo::params
define foo($x = $foo::params::x, $y = $foo::params::y, ...)
One important note
Before you happily proceed with the params.pp pattern I advise you to read this blog post: the problem with params.pp

Puppet Can't Find Variable for Template

Just getting started with Puppet, and I'm having trouble with my first template. It should be very easy, but I can't figure it out.
I have a module "base" at
/etc/puppet/modules/base/
./manifests
./manifests/service.pp
./manifests/init.pp
./manifests/params.pp
./manifests/config.pp
./manifests/install.pp
./templates
./templates/puppet.conf.erb
There's other stuff, but it's not necessary.
base/manifests/init.pp:
class base {
include base::install, base::service, base::config, base::params
}
base/manifests/config.pp
class base::config {
include base::params
File {
require => Class["base::install"],
ensure => present,
owner => root,
group => root,
}
file { "/etc/puppet/puppet.conf":
mode => 0644,
content => template("base/puppet.conf.erb"),
require => Class["base::install"],
nofity => Service["puppet"],
}
...
base/manifests/params.pp
class base::params {
$puppetserver = "pup01.sdirect.lab"
}
Finally the interesting part of the template at base/templates/puppet.conf.erb
...
server=<% puppetserver %>
The error message:
err: Failed to parse template base/puppet.conf.erb: Could not find
value for 'puppetserver' at
/etc/puppet/modules/base/manifests/config.pp:13 on node ...
I don't get what the problem is. I've copied this part straight out of the Pro Puppet book.
Could someone show me where $puppetserver should be defined and how?
The issue is that the name "puppetserver" needs to be fully qualified so Puppet can find the value, since it's defined in a different scope to the one the template is evaluated in.
The variable is defined in base::params so can only be referred to simply as "puppetserver" in that scope. When you're evaluating the template from within base::config, you're in a different scope and so you can't refer to the variable simply by its short name. The "include" adds the other class to the catalog, but doesn't change these rules.
This means to access it, you fully qualify it with the class name: base::params::puppetserver. If you were using it in the manifest itself, this would be $base::params::puppetserver. You'll see similar examples in Pro Puppet in the ssh::config and ssh::service classes where it refers to "ssh_service_name" in the params class (pages 43-45).
To access the variable in a template it's a bit different, use scope.lookupvar("base::params::puppetserver"). Taking your full example and adding a missing equals sign (to output the value) in the template:
...
server=<%= scope.lookupvar("base::params::puppetserver") %>
There's a bit more information about scoping on the Scope and Puppet as of 2.7 page.
(Edit: looks like it's listed on the confirmed errata page too with the same solution.)
Answer #1 is technically correct, but results in very verbose templates.
You can shorten them by bringing variable values from other classes into your own class scope:
class base::config {
include base::params
$puppetserver = $base::params::puppetserver
...
}
And then use them in your template as expected:
server=<% puppetserver %>
You could also use inherits:
class puppet::config inherits puppet::params {
....
In this way you don't have to define $puppetserver again in this class.

Resources