You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 12:35:31