厨师食谱执行的次序

鉴于以下配方:

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 

这是厨师在处理这个例子时要经过的步骤。

  1. 首先,编译ruby_block声明,这会导致一个名为ruby_block[block1]的资源被添加到资源集合中。 块的内容(第一个puts语句)还没有运行 – 当这个资源被收敛时它被保存为运行。
  2. 接下来,编译remote_file声明。 这会导致名为remote_file[/tmp/foo/]的资源被添加到资源集合中,其源为“ https://yahoo.com ”。 在编译这个声明的过程中,第二个puts语句将被执行 – 这在“remote_file”中有打印的副作用,但是不会影响放入资源集合的资源。
  3. 没有别的东西要编译,Chef开始汇集资源集合中的资源。 第一个是ruby_block[block1] ,Chef在块中运行ruby代码 – 在“block1”中打印。 在完成块的运行后,它会logging一条消息,说明资源已被调用。
  4. 最后,厨师汇聚remote_file[/tmp/foo] 。 同样,它logging与该活动相关联的消息(或两个)。

这应该产生以下输出序列:

  1. ruby_block编译时没有打印。
  2. 编译remote_file时将打印“remote_file”中的内容。
  3. ruby_block收敛时,将打印“block1”。
  4. 在ruby_block收敛之后,将会打印一个Chef日志消息。
  5. 其他Chef日志消息将在remote_file聚合期间/之后打印。

在你的第二个例子上:

 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的代码运行之前。

因此,这个配方的预期输出是:

  1. 编译ruby_block时没有输出
  2. 在编译remote_file时打印的“remote_file”中
  3. 由于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 

而这个配方的预期输出?

  1. 编译ruby_block时没有输出
  2. 在编译remote_file时打印的“remote_file”中
  3. 收集ruby_block时打印的“block1”
  4. 显示ruby_block的厨师日志消息被收敛
  5. 显示remote_file的厨师日志消息已被收敛

厨师有一个编译和运行阶段 。 我假定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就没有意义了。