鉴于以下配方:
ruby_block "block1" do block do puts "in block1" end action :create end remote_file "/tmp/foo" do puts "in remote_file" source "https://yahoo.com" end
我期望ruby_block首先运行(因为它首先),然后remote_file。
我想使用ruby_block来确定从下载的remote_file的URL,所以顺序是重要的。
如果不是我的puts()语句,我会假设这些按照预期的顺序运行,因为日志说:
==> default: [2014-06-12T17:49:19+00:00] INFO: ruby_block[block1] called ==> default: [2014-06-12T17:49:19+00:00] INFO: remote_file[/tmp/foo] created file /tmp/foo ==> default: [2014-06-12T17:49:20+00:00] INFO: remote_file[/tmp/foo] updated file contents /tmp/foo
但是在上面,我的puts()语句如下所示:
==> default: in remote_file ==> default: in block1
如果您认为资源按照预期的顺序运行,请考虑以下配方:
ruby_block "block1" do block do node.default['test'] = {} node.default['test']['foo'] ='https://google.com' puts "in block1" end action :create end remote_file "/tmp/foo" do puts "in remote_file" source node.default['test']['foo'] end
这个失败如下:
==> default: [2014-06-12T17:55:38+00:00] ERROR: {} is not a valid `source` parameter for remote_file. `source` must be an absolute URI or an array of URIs. ==> default: [2014-06-12T17:55:38+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
“block1”中的string不会出现在输出中,所以ruby_block永远不会运行。
所以问题是,我怎么能强制ruby_block运行,并首先运行?
好问题 – 你们的两个例子都是按照我所期望的方式工作的,但是为什么不是很明显。
正如StephenKing在他的回答中所写的那样,首先要了解的是食谱被编译(产生一组资源),然后资源被聚合(影响系统的变化)。 这两个阶段通常是交错的 – 在Chef完成编译所有配方之前,您的一些资源可能会被收敛。 Erik Hollensbe在他的文章“The Chef Resource Run Queue”中详细介绍了这一点。
这是你的第一个例子:
ruby_block "block1" do block do puts "in block1" end action :create end remote_file "/tmp/foo" do puts "in remote_file" source "https://yahoo.com" end
这是厨师在处理这个例子时要经过的步骤。
ruby_block[block1]的资源被添加到资源集合中。 块的内容(第一个puts语句)还没有运行 – 当这个资源被收敛时它被保存为运行。 remote_file[/tmp/foo/]的资源被添加到资源集合中,其源为“ https://yahoo.com ”。 在编译这个声明的过程中,第二个puts语句将被执行 – 这在“remote_file”中有打印的副作用,但是不会影响放入资源集合的资源。 ruby_block[block1] ,Chef在块中运行ruby代码 – 在“block1”中打印。 在完成块的运行后,它会logging一条消息,说明资源已被调用。 remote_file[/tmp/foo] 。 同样,它logging与该活动相关联的消息(或两个)。 这应该产生以下输出序列:
在你的第二个例子上:
ruby_block "block1" do block do node.default['test'] = {} node.default['test']['foo'] ='https://google.com' puts "in block1" end action :create end remote_file "/tmp/foo" do puts "in remote_file" source node.default['test']['foo'] end
和第一个例子一样,在编译ruby_block时,我们不希望打印任何内容 – 整个“块”被保存,其内容不会运行,直到资源被收敛。
我们看到的第一个输出是“in remote_file”,因为在Chef编译remote_file资源时执行puts语句。 在下一行中,我们将source参数设置为node.default['test']['foo'] ,这显然是{} 。 对于source这不是一个有效的值,所以Chef运行在这个点终止 – 在ruby_block的代码运行之前。
因此,这个配方的预期输出是:
source参数无效造成的错误 希望这有助于你理解你所看到的行为,但是我们仍然有一个问题需要解决。
虽然你问过“我怎样才能强制ruby_block先运行?”,你对StephenKing的评论暗示这并不是你想要的 – 如果你真的想让这个块先运行,你可以直接把它放到你的配方代码中。 或者,您可以使用.run_action()方法强制资源在编译后立即收敛 – 但是您认为在ruby_block有用之前还有更多资源需要收敛。
正如我们上面所看到的,资源不是“运行”的,它们首先被“编译”,然后“融合”。 考虑到这一点,你需要的是remote_file资源使用一些在编译时不知道的数据,但是当它被收敛时会被知道。 换句话说,像ruby_block的“block”参数 – 一段代码,直到后来才运行。 像这样的东西:
remote_file "/tmp/foo" do puts "in remote_file" # this syntax isn't valid... source do node.default['test']['foo'] end end
幸运的是,这样的事情确实存在 – 它被称为懒惰属性评估 。 使用该function,您的第二个示例将如下所示:
ruby_block "block1" do block do node.default['test'] = {} node.default['test']['foo'] = 'https://google.com' puts "in block1" end action :create end remote_file "/tmp/foo" do puts "in remote_file" source lazy { node['test']['foo'] } end
而这个配方的预期输出?
厨师有一个编译和运行阶段 。 我假定ruby_block里面的代码在编译阶段没有执行,因为它在block语句中(然后在运行阶段执行)。 然而, remote_file块中的放置位置在资源定义内部的“属性级别”,这实际上由厨师执行(实际上,afaik source node.default...是一个函数调用)。
所以,如果我做对了,你想要做的事情可以用下面的代码来完成:
node.default['test']['foo'] ='https://google.com' remote_file "/tmp/foo" do source node['test']['foo'] end
如果通过node.default设置属性不起作用,请使用node.set 。
另外提到我不通过node.default[]读取属性,而是直接通过node[]读取。 否则厨师的属性优先function就没有意义了。