我想向您展示我的用例,然后讨论可能的解决方案:
问题A: 我有2个食谱,“a”和“b”..“a”在我的文件系统上安装一些程序(比如“/usr/local/bin/stuff.sh”和食谱“b”需要运行这个并做输出的东西。
所以食谱“a”看起来像:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh"
(脚本只是回显“stdout”的东西)
和食谱“b”看起来像:
include_recipe "a"
var=`/usr/local/bin/stuff.sh`
(注意反引号,var
应包含stuff
)
现在我需要用它做一些事情,例如用这个用户名创建一个用户。所以在脚本“b”我添加
user "#{node[:var]}"
碰巧,这不起作用..显然厨师运行所有不是资源的东西然后运行资源,所以一旦我运行脚本厨师抱怨它无法编译,因为它首先尝试运行配方“b”处的“var = ...”行并且失败,因为配方a处的“执行...”尚未运行,因此“stuff.sh”脚本尚不存在。 毋庸置疑,这非常令人讨厌,因为它打破了“厨师从上到下按顺序运行”,这是我在开始使用它时所承诺的。 但是,我不是很挑剔所以我开始寻找这个问题的替代解决方案,所以:
问题B:我遇到了“ruby_block”的想法。显然,这是一种资源,因此它将与其他资源一起进行评估。我说好了,然后我想创建脚本,在“ruby_block”中获取输出,然后将其传递给“user”。所以食谱“b”现在看起来像:
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`
end
end
user "#{node[:var]}"
然而,事实证明变量(var
)没有从“ruby_block”传递给“user”并且它仍然是空的。无论我试图用它做什么杂耍,我都失败了(或者我只是没找到正确的杂耍方法)
对于厨师/红宝石主人:我如何解决问题A?我如何解决问题B?
答案 0 :(得分:6)
你已经用Ruby块解决了问题A.
现在你必须用类似的方法解决问题B:
ruby_block "create user" do
block do
user = Chef::Resource::User.new(node[:var], run_context)
user.shell '/bin/bash' # Set parameters using this syntax
user.run_action :create
user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
end
end
您还可以通过在编译阶段创建文件来解决问题A:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
action :nothing
end.run_action(:run)
如果遵循此行动方案,请确保:
/usr/local/bin
存在; stuff.sh
是可执行的; OR var=`sh /usr/local/bin/stuff.sh`
答案 1 :(得分:1)
执行此操作的现代方法是使用自定义资源:
在Cookbooks / create_script / resources / create_script.rb
provides :create_script
unified_mode true
property :script_name, :name_property: true
action :run do
execute "creating #{script_name}" do
command "echo 'echo stuff' > #{script_name}"
not_if { File.exist?(script_name) }
end
end
然后输入配方代码:
create_script "/usr/local/bin/stuff.sh"
对于第二种情况,我将避免完全使用节点变量:
script_location = "/usr/local/bin/stuff.sh"
create_script script_location
# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location
如果您需要将其移动到一个属性中并从不同的配方调用它,那么就不需要ruby_blocks或lazy:
一些食谱的attribute / default.rb文件(或策略文件等):
default['script_location'] = "/usr/local/bin/stuff.sh"
在配方代码或其他自定义资源中:
create_script node['script_location']
user node['script_location']
通过这种方法,无需懒惰或使用ruby_block。
答案 2 :(得分:0)
实际上,有几种方法可以解决您遇到的问题。
第一种方法是避免传递的块中出现范围问题,并执行类似操作。
include_recipe "a"
this = self
ruby_block "a_block" do
block do
this.user `/usr/local/bin/stuff.sh`
end
end
假设您只计划使用一次,那会很好。但是,如果您确实需要在节点上存储变量以用于其他用途,则可以依靠ruby内部的延迟调用来解决该问题。
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`.strip
end
end
user do
username lazy { "#{node[:var]}" }
end
您会很快与Chef一起注意到,对于这种情况,它具有所有默认假设的替代。