ruby 如何使用 Chef 懒惰地评估任意变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20620724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 06:19:38  来源:igfitidea点击:

How to lazily evaluate an arbitrary variable with Chef

rubyvariableschefchef-recipe

提问by Patrick M

I'm writing a Chef recipe to install our application code and execute it. The recipe needs to be particular about the directory this code ends up in (for running templates, setting log forwarding etc.). Thus the directory itself pops up in a lot of places in different recipes.

我正在编写一个 Chef 配方来安装我们的应用程序代码并执行它。该配方需要特别注意此代码最终所在的目录(用于运行模板、设置日志转发等)。因此,目录本身会在不同配方的许多地方弹出。

I am trying to fetch/define a variable so I can re-use it in my resource block with string interpolation. This is pretty straightforward:

我正在尝试获取/定义一个变量,以便我可以通过字符串插值在我的资源块中重新使用它。这非常简单:

home = node['etc']['passwd'][node['nodejs']['user']]['dir']

With an example usage being to run npm installwhile telling it to plunk the repo downloads in the home directory, like this:

示例用法是npm install在告诉它在主目录中插入 repo 下载时运行,如下所示:

execute "npm install" do
  command "npm install #{prefix}#{app} --prefix #{home}"
end

Except that the first block which defines the homevariable will run at compile time. On a fresh server, where my nodejs user account may not exist yet, this is a problem, giving a

除了定义home变量的第一个块将在编译时运行。在一个新的服务器上,我的 nodejs 用户帐户可能还不存在,这是一个问题,给出

NoMethodError undefined method '[]' for nil:NilClass

I have a few workarounds, but I would like a specific solution to make the homevariable only be fetched at recipe execute time, not compile time.

我有一些解决方法,但我想要一个特定的解决方案,使home变量只能在配方执行时而不是编译时获取。



Workaround 1

解决方法 1

Dynamically evaluate the home variable inside a ruby block, like so:

动态评估 ruby​​ 块内的 home 变量,如下所示:

ruby_block "fetch home dir" do
  block do
    home = node['etc']['passwd'][node['nodejs']['user']]['dir']
  end
end

This does not seem to actually work, giving a NoMethodError undefined method home for Chef::Resource::Directorywhen you try to do something like this:

这似乎并不实际工作,当您尝试执行以下操作时,会为 Chef::Resource::Directory提供一个NoMethodError 未定义的方法主页

directory ".npm" do
  path "#{home}/.npm"
end

I feel like I must be doing something wrong here.

我觉得我一定在这里做错了什么。

Workaround 2

解决方法 2

Lazily evaluate a parameter on every single resource that needs it. So instead do this:

懒惰地评估每个需要它的资源上的参数。所以改为这样做:

directory ".npm" do
  path lazy "#{node['etc']['passwd'][node['nodejs']['user']]['dir']}/.npm"
end

But it would be really great to just have to maintain that line of code once, store it in a variable and be done with it.

但是只需要维护那行代码一次,将它存储在一个变量中并完成它真的很棒。

Workaround 3

解决方法 3

Create the user at compile time. This of course works, using the notify trick linked here, like this:

在编译时创建用户。这当然有效,使用这里链接通知技巧,如下所示:

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)

This solves my problem exactly, but there are other cases where I can imagine wanting the ability to delay evaluation of a variable, so I let my question stand.

这完全解决了我的问题,但在其他情况下,我可以想象想要延迟评估变量的能力,所以我让我的问题成立。

What I would Like

我想要什么

I would really like to be able to do

我真的很想能够做到

home lazy = node['etc']['passwd'][node['nodejs']['user']]['dir']

But that's not legal syntax, giving NameError Cannot find a resource for home on ubuntu version 13.10(which is an odd syntax error, but whatever). Is there a legal way to accomplish this?

但这不是合法的语法,在 ubuntu 版本 13.10 上给出NameError 无法找到 home 的资源(这是一个奇怪的语法错误,但无论如何)。有没有合法的方法来实现这一点?

回答by thoughtcroft

I haven't tested this particular code but I have done something similar in cookbooks and used a lambda to delay evaluation as follows:

我还没有测试过这个特定的代码,但我在食谱中做了类似的事情,并使用 lambda 来延迟评估,如下所示:

home = lambda {node['etc']['passwd'][node['nodejs']['user']]['dir']}

execute "npm install" do
  command "npm install #{prefix}#{app} --prefix #{home.call}"
end

回答by Oscar Barrett

For ruby_block, any variables within the block will need to be global as anything defined within the block is local to it.

对于ruby_block,块中的任何变量都需要是全局的,因为块中定义的任何变量都是它的局部变量。

You can't use a lambda for delayed execution in a library, so ruby_blockworks well in this case.

您不能在库中使用 lambda 进行延迟执行,因此ruby_block在这种情况下效果很好。

回答by e.aktec

@thoughtcroft answer not working for me at chef-client 12.8.1

@thoughtcroft 回答在厨师客户端 12.8.1 对我不起作用

In this cases I place needed code into custom resource and call it with lazy attributes.

在这种情况下,我将所需的代码放入自定义资源中并使用惰性属性调用它。

mycookbook_myresource "name" do
  attribute lazy { myvar }
end

Not elegant solution but it works for me.

不是优雅的解决方案,但它对我有用。