厨师菜谱执行顺序重现

厨师菜谱执行顺序重现

给出以下配方:

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)

字符串“in block1”没有出现在输出中,因此 ruby​​_block 从未运行。

所以问题是,我怎样才能强制 ruby​​_block 运行,并且先运行?

答案1

好问题——您的两个例子都按照我预期的方式工作,但原因并不明显。

正如 StephenKing 在他的回复中所写,首先要理解的是,配方被编译(以生成一组资源),然后资源被聚合(以对系统进行更改)。这两个阶段通常是交错的 - 在 Chef 完成所有配方的编译之前,您的一些资源可能会被聚合。Erik Hollensbe 在他的帖子中详细介绍了这一点“Chef 资源运行队列”


这又是您的第一个例子:

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

这些是 Chef 在处理该示例时将经历的步骤。

  1. 首先,编译 ruby​​_block 声明,从而将名为 的资源ruby_block[block1]添加到资源集合中。块的内容(第一个puts语句)尚未运行 - 它被保存起来,以便在此资源收敛时运行。
  2. 接下来,编译 remote_file 声明。这将导致名为的资源remote_file[/tmp/foo/]被添加到资源集合中,其源为“https://yahoo.com“。在编译此声明的过程中,puts将执行第二条语句 - 这会产生打印“in remote_file”的副作用,但不会影响放入资源集合中的资源。
  3. 由于没有其他内容需要编译,Chef 开始聚合资源集合中的资源。第一个是ruby_block[block1],Chef 运行块中的 ruby​​ 代码 - 打印“in block1”。运行完块后,它会记录一条消息,说明已调用该资源。
  4. 最后,Chef 收敛remote_file[/tmp/foo]。它再次记录与该活动相关的一条(或两条)消息。

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

  1. 编译 ruby​​_block 时没有打印任何内容。
  2. 在编译 remote_file 时将打印“in remote_file”。
  3. 当 ruby​​_block 收敛时,将会打印“in 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”,因为该puts语句是在 Chef 编译 remote_file 资源时执行的。在下一行,我们将参数设置source为值node.default['test']['foo'],显然是{}。这不是有效的值source,因此 Chef 运行在该点终止 - 在代码运行之前ruby_block

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

  1. 编译 ruby​​_block 时无输出
  2. 编译 remote_file 时打印“in remote_file”
  3. source由于参数无效而导致的错误

希望这能帮助您理解您所看到的行为,但我们仍有一个问题需要解决。

虽然你问“我怎样才能强制 ruby​​_block 首先运行?”,但你对 StephenKing 的评论表明这并不是你真正想要的 - 如果你真的希望该块首先运行,你可以将其直接放入你的配方代码中。或者,你可以使用.run_action() 方法强制资源在编译时立即收敛 - 但您说在 ruby​​_block 可用之前还有更多资源需要收敛。

正如我们上面所看到的,资源不是“运行”的,而是先“编译”然后“聚合”的。考虑到这一点,您需要的是让资源remote_file使用一些在编译时未知但在聚合时已知的数据。换句话说,类似于“block”参数的东西ruby_block——一段直到稍后才运行的代码。像这样:

remote_file "/tmp/foo" do
    puts "in remote_file"
    # this syntax isn't valid...
    source do 
        node.default['test']['foo']
    end
end

幸运的是,这样的事确实存在——它被称为惰性属性评估使用该功能,您的第二个示例将如下所示:

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 时打印“in remote_file”
  3. 在收敛 ruby​​_block 时打印“in block1”
  4. Chef 日志消息显示 ruby​​_block 已聚合
  5. Chef 日志消息显示 remote_file 已聚合

答案2

厨师有一个编译和运行阶段。我假设 中的代码ruby_block不会在编译阶段执行,因为它位于语句中block(然后会在运行阶段执行)。但是,块puts中的remote_file位于资源定义中的“属性级别”,它实际上是由 chef 执行的(事实上,据我所知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[]。否则属性优先级厨师的功能毫无意义。

相关内容