如何在Puppet中正确生成哈希并遍历以创建指定组的目录?
问题描述
我是Puppet/Ruby编程新手,正尝试在模块的.pp清单文件中遍历哈希,使用键值对来创建目录。
初始双文件方案
使用两个同步的文本文件:
name_list.txt内容:
john mike tom
group_list.txt内容:
admins users tech
期望对应关系:
john -> admins mike -> users tom -> tech
自定义Facts实现
读取两个文件的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
合并为哈希的Fact:
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
清单代码
$hash_list.each |$dir_name, $new_group| { file { "/tmp/${dir_name}": ensure => directory, owner => 'root', group => $new_group, mode => '755', } }
错误情况
运行puppet agent时所有目录都使用group_list.txt最后一个条目tech,且报错找不到该组:
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
查看fact结果:
{ "hash_list": { "john": "tech", "mike": "tech", "tom": "tech" } }
单文件尝试的问题
改用new_list.txt(格式名称,组):
john,admins mike,users tom,tech
自定义Fact代码:
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
但返回的是数组而非哈希:
{ "test": [ "john,admins\n", "mike,users\n", "tom,tech\n" ] }
需要正确的哈希生成方法和清单遍历代码。
解决方案
方案1:修复双文件的哈希生成Fact
原哈希生成错误是因为嵌套循环:每个目录名会遍历所有组名,最终被最后一个组覆盖。正确做法是按索引一一对应,用Ruby的zip方法合并数组为键值对再转哈希。
修改后的hash_list Fact:
Facter.add(:hash_list) do setcode do names = Facter.value(:name_list) || [] groups = Facter.value(:group_list) || [] # 按索引配对,过滤掉长度不匹配的条目 names.zip(groups).to_h.reject { |k, v| k.nil? || v.nil? } end end
方案2:单文件生成哈希的正确Fact
原代码错误在于Array(File.readlines(...)) do |file|的写法,Array()直接返回数组,后续块不会执行。正确做法是遍历每一行并处理:
修改后的Fact:
Facter.add(:dir_group_map) do setcode do dir_group_map = {} if File.exist?("/tmp/new_list.txt") File.readlines("/tmp/new_list.txt").each do |line| line = line.chomp.strip # 去除换行符和首尾空格 next if line.empty? # 跳过空行 name, group = line.split(',', 2) # 按逗号分割,最多分2部分 next unless name && group # 跳过格式错误的行 dir_group_map[name.strip] = group.strip # 去除前后空格 end end dir_group_map end end
清单遍历哈希的正确代码
无论用哪种方案生成哈希,遍历逻辑都可沿用,只需确保变量名对应。以方案2的dir_group_map为例:
$dir_group_map.each |$dir_name, $new_group| { file { "/tmp/${dir_name}": ensure => directory, owner => 'root', group => $new_group, mode => '755', } }
注意事项
- 确保目标系统存在对应的组(如
admins、users、tech),否则会出现找不到组的错误。 - 自定义Fact文件需放在模块的
lib/facter目录下,确保Puppet能加载到。 - 可通过
puppet facts show dir_group_map(或对应哈希名)验证Fact是否生成正确。
内容的提问来源于stack exchange,提问作者Questioneer
相关产品推荐
相关产品推荐

