Duplicate declaration error with puppet - puppet

I try to create a folder then trying to copy some files into it like below.
init.pp
$tempfolder = "D:/TempFolder/"
file { [ $tempfolder ]:
ensure => "directory",
}
file { [ $tempfolder ]:
ensure => present,
recurse => true,
source => "E:/TestFiules",
}
When I try to run this, it is giving below error
Error: Duplicate declaration: File [ D:/TempFolder/ ] is already declared.
What is the wrong in the usage?

For reference: http://docs.puppetlabs.com/guides/techniques.html#how-can-i-manage-whole-directories-of-files-without-explicitly-listing-the-files
Thus you could do
file { "$tempfolder":
ensure => directory,
recurse => true,
source => "E:/TestFiules",
}
The ensure => directory also ensures that it will be present, so you don't have to declare it again.

A node can only have one resources with the same name declared, in this case the $tempfolder.
Either the $tempfolder is created empty (your first declaration) or created and populated with your E:/TestFiule content (the second declaration).
Note that you can drop the array syntax it's often used to create multiple directory at once or ensure an order like creating a tree

I came across this message when, but the error was:
Error: Duplicate declaration: File[] is already declared in file init.pp:40; cannot redeclare at init.pp:46 on node (redacted)
The file it was looking up was not defined, because my Hiera configuration was incorrect. As a result it was declaring two files named "".
Verify that Hiera is passing values correctly.

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.

Puppet nested resources create_resources, can't convert string into hash

trying to build a DNS with this module: ref. But getting this error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, can't convert String into Hash.
I have nested YAML, but not sure if it's correctly formatted or not or problems with something else within my code.
This is my dns profile dns.pp:
class profile::bind {
validate_hash($conf)
$conf = hiera_hash('bind::zone', undef)
create_resources('profile::bind::make::zone', $conf)
}
This is how I define my zone with make_zone.pp:
define profile::bind::make::zone (
$hash_data,
$zone,
$ensure,
$zone_contact,
$zone_ns,
$zone_serial,
$zone_ttl,
$zone_origin,
) {
validate_hash($hash_data)
bind::zone { $zone :
ensure => $ensure,
zone_contact => $zone_contact,
zone_ns => [$zone_ns],
zone_serial => $zone_serial,
zone_ttl => $zone_ttl,
zone_origin => $zone_origin,
}
}
This is my host1.yaml data:
---
version: 5
bind::zone:
zone: test.ltd
ensure: present
zone_contact: 'contact.test.ltd'
zone_ns:
-'ns0.test.ltd'
-'ns1.test.ltd'
zone_serial: '2018010101'
zone_ttl: '767200'
zone_origin: 'test.ltd'
hash_data:
"newyork":
owner: "11.22.33.44"
"tokyo":
owner: "22.33.44.55"
"london":
owner: "33.44.55.66"
bind::cname:
ensure: present
record_type: master
There are a number of mistakes and misunderstandings in the code. I fixed them up so that the code at least compiles and ended up with this.
Changes to profile::bind:
class profile::bind {
include bind
$conf = lookup('bind::zone')
create_resources(profile::bind::make::zone, $conf)
}
Changes to profile::bind::make::zone:
define profile::bind::make::zone (
Enum['present','absent'] $ensure,
String $zone_contact,
Array[String] $zone_ns,
String $zone_serial,
String $zone_ttl,
String $zone_origin,
Hash[String, Hash[String, String]] $hash_data,
) {
bind::zone { $name:
ensure => $ensure,
zone_contact => $zone_contact,
zone_ns => $zone_ns,
zone_serial => $zone_serial,
zone_ttl => $zone_ttl,
zone_origin => $zone_origin,
}
}
Changes to host1.yaml:
---
bind::zone:
'test.ltd':
ensure: present
zone_contact: 'contact.test.ltd'
zone_ns:
- 'ns0.test.ltd'
- 'ns1.test.ltd'
zone_serial: '2018010101'
zone_ttl: '767200'
zone_origin: 'test.ltd'
hash_data:
"newyork":
owner: "11.22.33.44"
"tokyo":
owner: "22.33.44.55"
"london":
owner: "33.44.55.66"
Some explanation:
immediate problem:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, can't convert String into Hash.
This error was caused because your Hiera data was not correctly structured as a Hash[String, Hash[String, String]]. Notice in the yaml I have removed your key "zone" and created a nested Hash there.
must include the bind class
The camptocamp BIND module requires the bind class to also be declared. See its documentation.
validate_hash function is legacy and in the wrong place
As John Bollinger mentioned in the comment, you had the validate_hash on the wrong line. I think that was a cut/paste issue, because you would have got a different error message if that was really your code. Anyway, since you're using Puppet 5 (I guess that by the version => 5 in your Hiera), don't use the legacy validate functions ; use Puppet's data type validation. So I just deleted that line.
use lookup() instead of hiera_hash()
Again, since you're using Puppet 5, use the lookup() function instead of the deprecated hiera_hash() function.
version 5 belongs in hiera.yaml, not host1.yaml
It won't cause you any problems, but the line version: 5 won't do anything here, and it belongs in your hiera.yaml file. I used a hiera.yaml file as follows for testing:
---
version: 5
defaults:
datadir: data
data_hash: yaml_data
hierarchy:
- name: "Host 1"
paths:
- host1.yaml
zone_ns type confusion
You had 2 problems with the zone_ns - firstly, a typo in your YAML (no space after the -) ; and secondly, you passed in an Array of zone NS's and then tried to coerce the array to an array in your defined type.
zone parameter should be the name var
Notice I had to delete the $zone parameter in your defined type, and I used the special $name variable instead, to get the name from the title.
refactored to use data type validation
Notice how I used Puppet's data type validation on your inputs in the defined type, and then I had no further need for the legacy validate_hash function and other related validate functions. Read more about that here.
I think that's all. Hope that helps!

Puppet tries to rename sites-avaialbe folder

I am running Puppet on staging server, for some reasons puppet starts trying removing sites-available folder and I have no idea why. Any hint will be helpful.
Error: Could not set 'file' on ensure: Is a directory # rb_file_s_rename - (/etc/nginx/sites-available20180808-12536-11p54v, /etc/nginx/sites-available) at 12:/etc/puppet/modules/nginx/manifests/vhost.pp
Error: Could not set 'file' on ensure: Is a directory # rb_file_s_rename - (/etc/nginx/sites-available20180808-12536-11p54v, /etc/nginx/sites-available) at 12:/etc/puppet/modules/nginx/manifests/vhost.pp
Code:
define nginx::vhost($docroot, $port = 80, $template = 'nginx/vhost_php.erb', $allow = [], $deny = [], $aliases = [])
{
include nginx
file { "/etc/nginx/sites-available/${name}":
owner => 'root',
group => 'root',
mode => 644,
content => template($template),
require => Package['nginx'],
notify => Service['nginx'],
}
file { "/etc/nginx/sites-enabled/${name}":
ensure => 'link',
target => "/etc/nginx/sites-available/${name}",
require => File["/etc/nginx/sites-available/${name}"],
notify => Service['nginx'],
}
}
As #MattSchuchard remarked in comments, the error messages show that Puppet thinks you've asked it to convert a directory into a file. Furthermore, it appears to be associating that action with the first File resource in your manifest, which declares
file { "/etc/nginx/sites-available/${name}":
# ...
}
You will note that that resource appears to be trying to manage a file inside the directory, rather than the directory itself, but that discrepancy would be resolved if the automagic $name variable happened to take an empty string as its value. That's what I presume is happening.
You don't show the relevant declaration(s) of the nginx::vhost resources, but I think you'll find that the problem is there. The (slightly) broader context of those error messages would probably confirm this diagnosis: it normally contains a path-like specification of the resource in which the error occurred, and that would include the relevant resource title.

How to work around vcsrepo "duplicate declaration" evaluation error?

I am installing from github using puppet-vcsrepo. The code looks something like this:
class demo_class(
$my_repo = undef,
$my_tag = undef,
){
vcsrepo { "$my_repo",
path => "/home/user/$my_repo",
source => 'git#github.com:7yl4r/$my_repo.git',
ensure => latest,
provider => git,
}
# then declare resources specific to my_tag
}
This works just fine when called only once, but I am iterating over a list and installing dependencies so this resource sometimes gets declared twice. I think this is roughly equivalent to the code below.
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag1,
}
class {"demo_class":
my_repo => test_repo,
my_tag => test_tag2,
}
Doing this yields a server-side "Duplicate declaration" error because vcsrepo is trying to map the the same path twice. However, this is exactly the behavior I want: for both resources declared by demo_class to require the same repo in the same location. This is so that I can declare one or more resources using demo_class and be sure the repo given by my_repo (which may be common to multiple my_tags) is there.
How can I modify this class so that I can call it twice without hitting an error?
I see the problem.
I reproduced the issue using this code:
define my_vcs_repo ($myRepo, $myTag) {
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo",
source => "git#github.com:7yl4r/$myRepo.git",
revision => $myTag,
ensure => latest,
provider => git,
}
}
$data = [
{
myRepo => testRepo,
myTag => testTag1,
},
{
myRepo => testRepo,
myTag => testTag2,
},
]
$data.each |$i, $ref| {
$myRepo = $ref['myRepo']
$myTag = $ref['myTag']
my_vcs_repo { "$myRepo-$i":
myRepo => $myRepo,
myTag => $myTag,
}
}
That then results in:
Puppet::PreformattedError:
Evaluation Error: Error while evaluating a Resource Statement, Evaluation Error: Error while evaluating a Resource Statement, Cannot alias Vcsrepo[testRepo-testTag2] to ["/home/user/testRepo"] at /
Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3; resource ["Vcsrepo", "/home/user/testRepo"] already declared at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/
manifests/init.pp:3 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:3:5 at /Users/alexharvey/git/modules/foo/spec/fixtures/modules/foo/manifests/init.pp:26 on node alexs-macbook-pro.local
The problem is that you are asking Puppet to clone the same Git module to a directory but with two different tags checked out. That does not make sense.
The fix is that you need to specify a unique path in the vcsrepo path attribute, e.g.:
vcsrepo { "$myRepo-$myTag":
path => "/home/user/$myRepo-$myTag",
source => "git#github.com:7yl4r/$myRepo.git",
revision => 'production',
ensure => latest,
provider => git,
}
By the way, I notice you are using camelCase for your variables. Don't do that. Aside from the fact that it is not idiomatic for Puppet, there are things that will break in some versions of Puppet/Rspec puppet that I have seen.
Use snake_case for your variable names and class parameter names.
Update
The question has been edited, and it is now a question about how to declare the same vcsrepo in more than one class.
In general, try to refactor so that you do not need to do this in the first place. In other words, just move it out of this class and put it somewhere that is only expected to be declared once.
If you cannot do this for some reason, then you can also use virtual resources, which will allow you to declare it in multiple classes that will be declared on the same node.
To do that, you just have to rewrite what you have there as:
#vcsrepo { $my_repo:
path => "/home/user/$my_repo",
source => "git#github.com:7yl4r/$my_repo.git",
ensure => latest,
provider => git,
}
realize Vcsrepo[$my_repo]
Keep in mind that you will not be able to declare the class demo_class twice on the same node either. You would need to turn it into a defined type, as I did above.
It is mentioned in the comments below that you can also use ensure_resource and ensure_resources; see docs in stdlib.

File paths must be fully qualified - Puppet

I have just started with puppet and I have an elementary question on using variables in a class. This is my class
# == Class: mysql::configure_mysql
class mysql::configure_mysql inherits mysql {
$mysql_backup = "/var/mysql_backup"
#Create directories
file { 'mysql_backup':
path => '($mysql_backup)',
ensure => 'directory',
}
}
I am getting an error
Error: Failed to apply catalog: Parameter path failed on File[mysql_backup]: File paths must be fully qualified, not '($mysql_backup)'
Can someone please help me with how to declare a variable and use it?
I have used this for reference: https://serverfault.com/questions/41577/how-to-fix-puppet-fully-qualified-parameter-path-error
Thanks
'' is a string literal in coding languages. You need the variable $mysql_backup to be resolved. Your resource was literally using the string ($mysql_backup). You also need to remove those parantheses as they are only a syntax error. For these reasons, '($mysql_backup)' needs to be changed to $mysql_backup. You could also put double quotes around the variable, but there would be no point to it.
file { 'mysql_backup':
path => $mysql_backup,
ensure => 'directory',
}
Puppet also uses the title of a file resource as the path, so you could shorten your resource to:
file { $mysql_backup: ensure => 'directory' }
The file's title must be the absoloute path to your file.
file { '/your/full/file/path/here':
path => "${mysql_backup}"
ensure => 'directory',
}
The path should be set with:
path => $mysql_backup,
The reason it didn't work in your case was that variables (like $mysql_backup) are not interpolated in single quotes. Changing the single quotes to double quotes would help, but then you would also need to remove the extraneous (..) brackets.

Resources