如何将哈希传递给 Puppet 中的自定义函数?

How do I pass a hash to a custom function in puppet?

我已经根据这里非常简单的例子定义了一个自定义函数:https://docs.puppet.com/guides/custom_functions.html

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    filename = args[0]
    hash_to_be_transformed = args[1]
    File.open(filename, 'a') {|fd| fd.puts hash_to_be_transformed }
  end
end

这有点管用。我可以这样称呼它:

  $my_hash = { key => "value1" , key2 => "value2" }
  notify{ "new hash!! $my_hash" :}
  transform_service_hash('/var/tmp/blah',$my_hash)

文件显示:

mgt21 ~ # cat /var/tmp/blah
keyvalue1key2value2

但是,如果我尝试访问散列的元素,则没有任何变化:

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    filename = args[0]
    hash_to_be_transformed = args[1]
    element1 = hash_to_be_transformed["key"] 
    File.open(filename, 'a') {|fd| fd.puts element1 }
  end
end

上面的块将完全相同的数据输出到 /var/tmp/blah。

而且,有趣的是,如果我删除文件名传递并在模块中静态定义它:

  $my_hash = { key => "value1" , key2 => "value2" }
  notify{ "new hash!! $my_hash. element1 is: $my_hash.key" :}
  transform_service_hash($my_hash)

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    hash_to_be_transformed = args[0]
    element1 = hash_to_be_transformed["key"] 
    File.open('/var/tmp/blah2', 'a') {|fd| fd.puts element1 }
  end
end

我收到以下错误:"Error 400 on SERVER: can't convert Hash into String" 行引用指向 "transform_service_hash($my_hash)"

我对 puppet 和 ruby 都是新手...所以我不确定我没有正确传递元素,如果我没有正确接收它,或者如果它是 puppet 无法处理的东西。请注意,我使用的是 puppet 的 3.8 版和 ruby.

的 1.8.7 版

感谢您的帮助。我一直在反对这个,google 还没有出现。

---编辑以阐明我的目标(我也对我的代码进行了一些特殊性的编辑):我试图将散列传递到 puppet 中的自定义 ruby 函数中。 "test" 散列有两个元素:一个字符串和一个数组。它是这样定义的:

  $my_hash = { key => "value1" , key2 => ['array_value1', 'array_value2'] }
  $my_display_element=$my_hash["key2"][0]
  notify{ "new hash!! $my_hash. the first value of the array stored in element2 is: $my_display_element" :}
  transform_service_hash('/var/tmp/blah',$my_hash)

函数如下所示:

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    filename = args[0]
    hash_to_be_transformed = args[1]
    element1 = args[1]["key"]
    element2 = args[1]["key2"][0]
    #element1 = hash_to_be_transformed["key"]
    #element2 = hash_to_be_transformed["key2"][0] 
    File.open(filename, 'a') {|fd| fd.puts "hash_to_be_transformed: #{hash_to_be_transformed}\n" }
    File.open(filename, 'a') {|fd| fd.puts "element1: #{element1}\n" }
    File.open(filename, 'a') {|fd| fd.puts "element2: #{element2}\n" }        
  end
end

现在,我只想看到我能够像访问散列一样访问传递的散列中的元素。所以我希望输出文件看起来像:

hash_to_be_transformed: keyvalue1key2array_value1array_value2
element1: value1
element2: array_value1

但是,在输出文件中,我看到:

mgt21 ~ # cat /var/tmp/blah 
keyvalue1key2array_value1array_value2

很明显,这里有些不对劲,因为我的文本没有被添加,完整的散列只打印一次,而且看起来是字符串形式。

我认为这可能与我没有传入文件名时出现的错误有关(见上文)。我认为我的散列被解释(或传递)为字符串,因此我无法访问这些元素。不幸的是,我仍然无法验证这一点或弄清楚为什么会发生这种情况。

---根据下面马特的回答编辑2。

我决定简化我的代码以隔离这个 "can't convert Hash into String error"。我还进行了他建议的更改,以消除我的密钥声明中的歧义。

  $my_hash = { 'key' => "value1" , 'key2' => ['array_value1', 'array_value2'] }
  $my_display_element=$my_hash["key2"][0]
  notify{ "new hash!! $my_hash. the first value of the array stored in element2 is: $my_display_element" :}
  transform_service_hash($my_hash)

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    hash_to_be_transformed = args[0]
    element1 = args[0]['key']
    element2 = args[0]['key2'][0]
    File.open('/var/tmp/blah', 'a') {|fd| fd.puts "hash_to_be_transformed: #{hash_to_be_transformed}\n" }
    File.open('/var/tmp/blah', 'a') {|fd| fd.puts "element1: #{element1}\n" }
    File.open('/var/tmp/blah', 'a') {|fd| fd.puts "element2: #{element2}\n" }
  end
end

但是,我仍然得到相同的 "Hash to String error"。值得注意的是,我还尝试将哈希简化为:

  $my_hash = { 'key' => "value1" , 'key2' => "value2" }

我仍然得到 "Hash to String error"。

我很快将您的自定义解析器函数转换为纯 ruby,如下所示:

hash = { 'key' => 'value1', 'key2' => %w(array_value1 array_value2) }

def newfunction(filename, a_hash)
  element1 = a_hash['key']
  element2 = a_hash['key2'][0]
  File.open(filename, 'a') do |fd|
    fd.puts "hash_to_be_transformed: #{a_hash}"
    fd.puts "element1: #{element1}"
    fd.puts "element2: #{element2}"
  end
end

newfunction('foo.txt', hash)

这会产生如下所示的输出文本文件:

hash_to_be_transformed: {"key"=>"value1", "key2"=>["array_value1", "array_value2"]}
element1: value1
element2: array_value1

这似乎证实了我最初对这里出了什么问题的怀疑。您在 Puppet 中的哈希值:

$my_hash = { key => "value1" , key2 => ['array_value1', 'array_value2'] }

有 implicit/ambiguous 类型的键。在我用来测试的 ruby 代码中,我明确地将它们建立为字符串。这也与您代码中的这些行失败密切相关:

element1 = args[1]["key"]
element2 = args[1]["key2"][0]

以及您的错误信息:

Error 400 on SERVER: can't convert Hash into String

因为您在 ruby 代码中指定您希望键为字符串。将 Puppet 中的哈希更改为:

$my_hash = { 'key' => "value1" , 'key2' => "value2" }

应该解决这个问题。

顺便说一句,我建议使用 linters 来帮助您学习这些语言。 Puppet-Lint、Rubocop 和 Reek 都会帮助指出代码中次优和混乱的部分,以帮助您学习新语言。

在相关说明中,您可能希望在自定义解析器函数的顶部放置类似这样的内容:

raise(Puppet::ParseError, 'newfunction expects two arguments') if args.length != 2

在咬牙切齿(以及来自@MattSchuchard 的一些非常有用的指示)之后,我意识到 none 对我的功能所做的更改正在生效。每次更改自定义函数后都需要重新启动 puppetmaster 服务:docs.puppet.com/guides/custom_functions.html(在 "Gotchas" 下适当)。

一旦我在每次更改函数后开始重新启动此服务,我的哈希就能够被正确解析:

来自 .pp 文件:

  $filename = "/var/tmp/test"
  $my_hash = { 'key' => "value1" , 'key2' => ["M\'lady\n*doffs cap*", 'array_value2'] }
  transform_service_hash($filename, $my_hash)

来自 ruby 文件:

module Puppet::Parser::Functions
  newfunction(:transform_service_hash) do |args|
    filename = args[0]
    hash_to_be_transformed = args[1]
    array_val = hash_to_be_transformed['key2'][0]
    File.open(filename, 'a') {|fd| fd.puts "#{array_val}\n" }
  end
end

并输出:

mgt21 tmp # cat test 
M'lady
*doffs cap*