我正在编写一个Chef配方来安装我们的应用程序代码并执行它。配方需要特别关注此代码最终的目录(用于运行模板,设置日志转发等)。因此,目录本身会在不同的食谱中弹出很多地方。
我正在尝试获取/定义变量,因此我可以在我的资源块中使用字符串插值重新使用它。这非常简单:
home = node['etc']['passwd'][node['nodejs']['user']]['dir']
使用示例用法运行npm install
,同时告诉它在主目录中插入repo下载,如下所示:
execute "npm install" do
command "npm install #{prefix}#{app} --prefix #{home}"
end
除了定义home
变量的第一个块将在编译时运行。在新的服务器上,我的nodejs用户帐户可能尚不存在,这是一个问题,给出了
NoMethodError undefined method '[]' for nil:NilClass
我有一些解决方法,但我想要一个特定的解决方案,使 home 变量只能在配方执行时获取,而不是在编译时获取。
动态评估ruby块内的home变量,如下所示:
ruby_block "fetch home dir" do
block do
home = node['etc']['passwd'][node['nodejs']['user']]['dir']
end
end
这似乎不起作用,当您尝试执行以下操作时,为Chef :: Resource :: Directory 提供 NoMethodError未定义方法主页:
directory ".npm" do
path "#{home}/.npm"
end
我觉得我必须在这里做错事。
懒惰地评估每个需要它的资源的参数。所以请这样做:
directory ".npm" do
path lazy "#{node['etc']['passwd'][node['nodejs']['user']]['dir']}/.npm"
end
但是,只需要维护一行代码,将其存储在变量中并完成它就会非常棒。
在编译时创建用户。这当然可以使用notify trick linked here,如下所示:
u = user node['nodejs']['user'] do
comment "The #{node['nodejs']['user']} is the user we want all our nodejs apps will run under."
username node['nodejs']['user']
home "/home/#{node['nodejs']['user']}"
end
u.run_action(:create)
这完全解决了我的问题,但是在其他情况下我可以想象想要延迟评估变量的能力,所以我让我的问题保持不变。
我真的希望能够做到
home lazy = node['etc']['passwd'][node['nodejs']['user']]['dir']
但这不是合法的语法,给出 NameError无法在ubuntu版本13.10上找到home的资源(这是一个奇怪的语法错误,但无论如何)。有没有合法的方法来实现这个目标?
答案 0 :(得分:18)
我没有测试过这个特殊的代码,但我在cookbook中做了类似的事情,并使用lambda来推迟评估,如下所示:
home = lambda {node['etc']['passwd'][node['nodejs']['user']]['dir']}
execute "npm install" do
command "npm install #{prefix}#{app} --prefix #{home.call}"
end
答案 1 :(得分:1)
对于ruby_block
,块中的任何变量都需要是全局的,因为块中定义的任何变量都是本地变量。
您无法在库中使用lambda延迟执行,因此ruby_block
在这种情况下效果很好。
答案 2 :(得分:1)
@thoughtcroft回答不适合我在chef-client 12.8.1
在这种情况下,我将所需的代码放入自定义资源中,并使用惰性属性调用它。
mycookbook_myresource "name" do
attribute lazy { myvar }
end
不是优雅的解决方案,但它对我有用。