循环制作厨师(个展)食谱的正确方法是什么?


8

有人可以告诉我厨师如何工作吗?这是一个相当广泛的问题,因此要缩小范围,我有一个非常简单的方法,可以遍历用户列表,并在每个用户不存在的情况下创建它们。这没用。

据我所知,循环似​​乎正在发生,正如我所期望的那样。循环完成后,将执行我的bash命令以创建每个用户,对于循环中的每个迭代都执行一次。但是,执行bash命令时,它们似乎只具有第一次循环迭代中的用户值。

类似于本示例,编写循环变量数据的配方的正确方法是什么?

这是食谱:

node[:users].each do |user|
  puts "in loop for #{user['username']}"
  bash "create_user" do
    user "root"
    code do
      puts "running 'useradd' for #{user['username']}"
      "useradd #{user['username']}"
    end
    not_if do
      puts "checking /etc/passwd for #{user['username']}"
      "cat /etc/passwd | grep #{user['username']}"
    end
  end
end

我正在使用Vagrant通过以下设置进行测试:

Vagrant::Config.run do |config|
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.provision :chef_solo do |chef|
    chef.add_recipe "sample"
    chef.json = {
      :users => [
        {:username => 'testA'},
        {:username => 'testB'},
        {:username => 'testC'},
        {:username => 'testD'},
        {:username => 'testE'},
      ],
    }
  end
end

配方中的puts语句生成的消息如下所示:

2013-03-08T01:03:46+00:00] INFO: Start handlers complete.
in loop for testA

in loop for testB

in loop for testC

in loop for testD

in loop for testE

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Chef Run complete in 0.026071 seconds

Answers:



10

您可以通过了解Chef客户端运行中两个关键阶段之间的差异来解释您所看到的行为:编译和收敛。

在“编译”阶段,Chef客户端运行您食谱中的代码以构建资源集合。这是您已告知Chef在系统上管理的资源及其目标状态的列表。例如,一个说/tmp/foo应该存在的目录资源,并由root拥有:

directory "/tmp/foo" do
  owner "root"
end

在“收敛”阶段,Chef客户端使用提供程序加载每个资源的当前状态,然后将其与目标状态进行比较。如果不同,Chef将更新系统。对于我们的目录资源,Chef将创建目录(如果该目录不存在),并在必要时将其所有者更改为“ root”。

资源由它们的名称和类型唯一标识-我们的目录为directory[/tmp/foo]。当您拥有两个名称相同但属性不同的资源时,就会发生奇怪的事情-这可以解释您的问题,并且可以使用Darrin Holst的答案来解决:

node[:users].each do |user|
  puts "in loop for #{user['username']}"
  bash "create_user_#{user}" do
    user "root"
    code do
      puts "running 'useradd' for #{user['username']}"
      "useradd #{user['username']}"
    end
    not_if do
      puts "checking /etc/passwd for #{user['username']}"
      "cat /etc/passwd | grep #{user['username']}"
    end
  end
end

但是,在这种情况下,您将受益于使用Chef的用户资源。这是您的食谱的替代品(不带调试消息):

node[:users].each do |u|
  user u['username'] do
    action :create
  end
end

为什么这比一组bash资源更好?

  1. User资源在各种平台上的工作方式相同-相同的配方将在使用“ useradd”以外的东西来创建用户的操作系统上工作。
  2. 负责此操作的提供程序知道如何检查用户是否已经存在,因此您不需要not_if。
  3. 相同的提供程序也可以用于删除用户,锁定或解锁其密码以及更新现有用户帐户上的其他属性。

但是,使用正确资源的最佳理由是,它可以更清晰地传达您的意图。您的目标可能不是运行一堆Shell命令,而是要确保系统上存在某些用户。用于执行此操作的命令只是实现细节。

提供者封装了这些细节,使我们可以自由地专注于描述我们想要的东西。


1
谢谢,这使事情变得很清楚。用户创建配方只是一个示例,我可以使用它来更好地了解为什么我在其他地方遇到的循环无法按预期工作。
马修·J·莫里森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.