有人能向我解释一下厨师的工作原理吗? 这是一个相当广泛的问题,所以要缩小它的范围,我有这个非常简单的配方,循环用户列表,并创build每个用户,如果他们不存在。 这是行不通的。
从我可以告诉循环似乎正在发生,如我所料。 一旦循环已经完成我的bash命令来创build每个用户执行,每循环一次迭代。 但是,执行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
我使用以下设置使用Vagranttesting:
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
使您的脚本名称独特…
bash "create_user_#{user}" do
FWIW,我已经多次使用https://github.com/fnichol/chef-user ,它允许你创build/删除基于属性和数据库的用户。
您所看到的行为可以通过了解Chef客户端运行的两个关键阶段(编译和收敛)之间的差异来解释。
在“编译”阶段,Chef客户端运行食谱中的代码来构build资源集合 。 这是您告诉Chef在您的系统上pipe理的资源以及目标状态的列表。 例如,一个目录资源说/tmp/foo应该存在,并由root拥有:
directory "/tmp/foo" do owner "root" end
在“融合”阶段,Chef客户端使用提供者加载每个资源的当前状态,然后将其与目标状态进行比较。 如果不同,厨师会更新系统。 对于我们的Directory资源,Chef会创build一个不存在的目录,如有必要,将其所有者改为“root”。
资源是由它们的名称和types唯一标识的 – 我们的目录将是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
但是,在这种特殊情况下,您将从使用厨师的用户资源中受益。 这里是你的配方replace(没有debugging信息):
node[:users].each do |u| user u['username'] do action :create end end
为什么这比一组bash资源更好?
但使用正确的资源的最好的理由是它更清楚地传达你的意图。 你的目标可能不是运行一堆shell命令 – 这是为了确保你的系统上有一些用户。 用来做这些的命令只是一个实现细节。
提供者封装了这些细节,让我们可以自由地专注于描述我们想要的东西。