Search code examples
rubypuppet

How to iterate through a hash made of two arrays in Puppet and use both key and value in a module .pp file


I'm new to Puppet ruby coding and I'm struggling trying to iterate through a hash so I can use both key and value in a .pp file in the module to create directories.

So far, I have two text files that contain the names of the directories to be created and the groups to be used to create them.

name_list.txt

john
mike
tom

group_list.txt

admins
users
tech

Both files are synced, so the first entry in names_list.txt has its counterpart in group_list.txt as the first entry as well. In this way I should be able later to create the following directories with these groups:

john -> admins
mike -> users
tom  -> tech

I access those files using two custom facts:

Facter.add('name_list') do
  setcode do
    if File.exist?("/tmp/name_list.txt")
      Array(File.readlines("/tmp/name_list.txt").map{|l| l.chomp})
    end
  end
end
Facter.add('group_list') do
  setcode do
    if File.exist?("/tmp/group_list.txt")
      Array(File.readlines("/tmp/group_list.txt").map{|l| l.chomp})
    end
  end
end

and combine them in another custom fact to create the hash:

Facter.add(:hash_list) do
  setcode do
    hash_list = {}
    Facter.value(:name_list).each do |dir_name|
      Facter.value(:group_list).each do |group_name|
        hash_list[dir_name] = group_name
      end
    end
    hash_list
  end
end

Finally, the code part in the manifest .pp file that should create the directories is this one:

$hash_list.each |$dir_name, $new_group| {
      file { "/tmp/${dir_name}":
        ensure => directory,
        owner  => 'root',
        group  => $new_group,
        mode   => '755',
      }
}

When I run puppet agent, I have the below error that shows that the iteration through the group_list is trying to use the last entry in the file only:

Error: Could not find group tech
Info: Unknown failure using insync_values? on type: File[/tmp/john] / property: group to compare values ["tech"] and 0
Error: /Stage[main]/Temp::Test/File[/tmp/john]/group: change from 'root' to 'tech' failed: Could not find group tech
Error: Could not find group tech
Info: Unknown failure using insync_values? on type: File[/tmp/mike] / property: group to compare values ["tech"] and 0
Error: /Stage[main]/Temp::Test/File[/tmp/mike]/group: change from 'root' to 'tech' failed: Could not find group tech
Error: Could not find group tech
Info: Unknown failure using insync_values? on type: File[/tmp/tom] / property: group to compare values ["tech"] and 0
Error: /Stage[main]/Temp::Test/File[/tmp/tom]/group: change from 'root' to 'tech' failed: Could not find group tech

The below shows that indeed the :hash_list hash is not returning the right group for every directory:

# puppet facts hash_list
{
  "hash_list": {
    "john": "tech",
    "mike": "tech",
    "tom": "tech"
  }
}

I have done lots of tests trying to correct it but I wasn't able to manage it due to my lack of experience in Ruby/Puppet code.

Can someone explain me how to do it properly?

EDIT

After John Bollinger's answer, I saw the need of make my question more clear and remake it. I think that a better approach in my case should be to use a flat file that contains "name,group" like this: (Note: Just to clarify that that file is created and accessed in other server but for testing purposes I'm doing it in the /tmp folder for now and I still need it as the source of that data.)

john,admins
mike,users
tom,tech

and use a custom fact to read those contents so they can be passed to the manifest .pp file I mentioned before and iterate from there.

My problem again is that custom fact. I tried the below but is not giving me the expected result so I would appreciate some help with the correct code:

Facter.add('test') do
  setcode do
    file_data = {}
    if File.exist?("/tmp/new_list.txt")
      Array(File.readlines("/tmp/new_list.txt")) do |file|
        file.each do |line|
          line_data = line.split(',')
          file_data[line_data[0]] = line_data[1]
        end
      end
    end
  end
end

I was expecting a hash so I could iterate in the manifest as stated earlier but it seems I'm having what looks like an array:

    {
  "test": [
    "john,admins\n",
    "mike,users\n",
    "tom,tech\n"
  ]
}

Since this probably is not the best way to do this, I'm open to better solutions so any help would be really appreciated.


Solution

  • I finally found the solution to my question by applying the recommended approach in this comment here: Using Ruby, Reading a file, containing name/value pairs into a hash

    It worked like a charm.

    Thanks!