Может кто-нибудь объяснить мне, как работает шеф-повар? Это довольно широкий вопрос, поэтому, чтобы сузить его, у меня есть очень простой рецепт, который перебирает список пользователей и создает каждого из них, если они еще не существуют. Это не работает.
Из того, что я могу сказать, похоже, что цикл происходит, как я и ожидал. После завершения цикла мои команды 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
Сообщения, которые генерируются операторами put в рецепте, выглядят следующим образом:
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
Наблюдаемое вами поведение можно объяснить, понимая разницу между двумя ключевыми этапами выполнения клиента Chef: компиляцией и конвергенцией.
На этапе «компиляции» клиент Chef запускает код в ваших рецептах для создания сбор ресурсов. Это список Ресурсы которые вы сказали Chef управлять в вашей системе, вместе с их целевым состоянием. Например, ресурс Справочника, чтобы сказать, что /tmp/foo
должен существовать и принадлежать root:
directory "/tmp/foo" do
owner "root"
end
На этапе «конвергенции» клиент Chef использует провайдеры для загрузки текущего состояния каждого ресурса, а затем сравнивает его с целевым состоянием. Если они разные, Chef обновит систему. Для нашего ресурса Directory Chef создаст каталог, если он не существует, и при необходимости изменит его владельца на «root».
Ресурсы однозначно идентифицируются по их имени и типу - наш Справочник будет directory[/tmp/foo]
. Когда у вас есть два ресурса с одинаковым именем, но разными атрибутами, могут произойти странные вещи - это объясняет вашу проблему, и ее можно исправить с помощью ответа Даррина Холста:
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 User. Вот замена вашего рецепта (без отладочных сообщений):
node[:users].each do |u|
user u['username'] do
action :create
end
end
Почему это лучше, чем набор ресурсов bash?
Но лучшая причина использовать правильные ресурсы - это то, что они более четко передают ваши намерения. Вероятно, ваша цель заключается не в том, чтобы запустить кучу команд оболочки, а в том, чтобы убедиться, что в вашей системе присутствуют некоторые пользователи. Команды, используемые для этого, являются лишь деталью реализации.
Провайдеры инкапсулируют эти детали, позволяя нам сосредоточиться на описании того, что мы хотим.
сделайте имя вашего скрипта уникальным ...
bash "create_user_#{user}" do
FWIW, я использовал https://github.com/fnichol/chef-user несколько раз, что позволяет создавать / удалять пользователей на основе атрибутов и тегов данных.