执行 redux 的 Chef 配方顺序

Dan*_*aum 11 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
Run Code Online (Sandbox Code Playgroud)

我希望 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
Run Code Online (Sandbox Code Playgroud)

但除此之外,我的 puts() 语句如下:

==> default: in remote_file
==> default: in block1
Run Code Online (Sandbox Code Playgroud)

如果您认为资源按预期顺序运行,请考虑以下方法:

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
Run Code Online (Sandbox Code Playgroud)

这个失败如下:

==> 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)
Run Code Online (Sandbox Code Playgroud)

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

所以问题是,如何强制 ruby​​_block 运行,并先运行?

zts*_*zts 15

好问题 - 您的两个示例都按照我的预期工作,但原因并不是很明显。

正如 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
Run Code Online (Sandbox Code Playgroud)

这些是 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
Run Code Online (Sandbox Code Playgroud)

与第一个示例一样,我们不希望在编译 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资源使用一些在编译时未知但在收敛时已知的数据。换句话说,类似于ruby_block- 一段代码中的“块”参数,直到稍后才会运行。像这样的东西:

remote_file "/tmp/foo" do
    puts "in remote_file"
    # this syntax isn't valid...
    source do 
        node.default['test']['foo']
    end
end
Run Code Online (Sandbox Code Playgroud)

幸运的是,确实存在这样的东西——它叫做惰性属性评估。使用该功能,您的第二个示例将如下所示:

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
Run Code Online (Sandbox Code Playgroud)

这个食谱的预期输出是什么?

  1. 编译 ruby​​_block 时没有输出
  2. 编译 remote_file 时打印“in remote_file”
  3. 收敛 ruby​​_block 时打印“in block1”
  4. 显示 ruby​​_block 已收敛的 Chef 日志消息
  5. 显示 remote_file 已聚合的 Chef 日志消息


Ste*_*ing 0

Chef 有一个编译阶段和一个运行阶段。我假设内部的代码ruby_block在编译阶段不会执行,因为它位于block语句内部(然后将在运行阶段执行)。puts然而,块内部位于remote_file资源定义内的“属性级别”,它实际上由厨师执行(事实上,据我所知,这source node.default...是一个函数调用)。

所以如果我做对了,你想做的事情可以用下面的代码来完成:

node.default['test']['foo'] ='https://google.com'

remote_file "/tmp/foo" do
  source node['test']['foo']
end
Run Code Online (Sandbox Code Playgroud)

如果通过设置属性node.default不起作用,请使用node.set.

还要提一下,我不是通过 读取属性node.default[],而是直接通过 读取属性node[]。否则Chef 的属性优先级特征就没有意义。