I want to create files inside a defined type. I have tried it several ways but couldn't solve the issue. Let me explain you my case.
I am creating some files using temapltes and I could do that operation perfectly. I am using below ruby function to gather filename, location kind of data
require 'rexml/document'
include REXML
module Puppet::Parser::Functions
newfunction(:getConfigFileDetails, :type => :rvalue ) do |args|
fileDetails= []
doc = REXML::Document.new args[0]
doc.elements.each("node/congfigurations/config") {
|config|
fileName= config.elements["@fileName"].value
fileLocation= config.elements["@location"].value
fileDetails << {'filename' => fileName, 'filelocation'=> fileLocation}
}
return fileDetails
end
end
And I use this function inside my puppet class and it works fine for me
define fill_templates() {
$fileName = $name["filename"]
$fileLocation = $name['filelocation']
file { "${fileLocation}/${fileName}/":
ensure => present,
owner => 'root',
group => 'root',
mode => '0777',
content => template("config/${fileName}.erb"),
require => Exec["unzip_pack"],
}
}
$configFileDetails = getConfigFileDetails($allConfigurations['configurations'])
fill_templates { $configFileDetails: }
Then I tried to create files with my own content in them not get data from templates. Following is my ruby function
require 'rexml/document'
include REXML
module Puppet::Parser::Functions
newfunction(:getCreateFileDetails, :type => :rvalue ) do |args|
fileDetails= []
doc = REXML::Document.new args[0]
doc.elements.each("node/create/file") {
|filedata|
fileName= filedata.elements["filename"].text
fileLocation= filedata.elements["location"].text
fileContent= filedata.elements["content"].text
fileDetails << {'filename' => fileName, 'filelocation'=> fileLocation, 'filecontent'=> fileContent }
}
return fileDetails
end
end
And I use it inside my puppet class as follows
define create_files() {
$fileName = $name["filename"]
$fileLocation = $name['filelocation']
$fileContent = $name['filecontent']
file { "${fileLocation}/${fileName}/":
ensure => present,
owner => 'root',
group => 'root',
content => "$fileContent",
}
}
$createFileDetails = getCreateFileDetails($allConfigurations['configurations'])
create_files { $createFileDetails: }
But it always gives me error
Could not retrieve catalog from remote server: Could not intern from pson: Could not convert from pson: Could not find relationship target
I cannot realize the cause for this issue. What is the reason the previous template one working and the later one is not working.
Thank you for your light on this
I won't even try to understand how your first approach ever worked. It defies some basic rules of Puppet manifest semantics.
Basically, if you want to import a data structure (from anywhere, really) and spawn resources from that (through a defined or a native type), it should be structured as a hash:
{
'resource-name1' => {
'attribute-name1' => 'value',
'attribute-name2' => 'value',
...
},
'resource-name2' => {
...
}
}, ...
Building an array like
[
{ attribute1 => value, attribute2 => value, ... },
{ ... },
]
And passing each as the name for a resource is not valid by a long shot.
To fix your issue, make the custom function return an appropriate hash. (Renaming to avoid camel case - not a Puppet custom.)
require 'rexml/document'
include REXML
module Puppet::Parser::Functions
newfunction(:get_config_file_details, :type => :rvalue ) do |args|
result = {}
doc = REXML::Document.new args[0]
doc.elements.each("node/create/file") { |filedata|
name = filedata.elements["filename"].text
location = filedata.elements["location"].text
conent = filedata.elements["content"].text
result[name] = {
'fileLocation' => location,
'fileContent' => content,
}
}
result
end
end
Your defined types needs to accept actual parameters. (Aside: create_files
is a very bad name for a type - type names should be singular, and descriptive of what each resource instance represents.)
define my_config_file($fileName=$name, $fileLocation, $fileContent) {
file { "${fileLocation}/${fileName}/":
...
}
Finally, to create the appropriate instances from the hash, use the create_resources
function.
$my_config_file_data = get_config_file_details($xml)
create_resources('my_config_files', $my_config_file_data)
Final advice: If this is at all possible, serialize your data in an appropriate YAML or JSON format instead of XML. You can make those available to Hiera and load them directly through the hiera()
function - no custom Ruby code needed.