torne seu nome de script exclusivo ...
bash "create_user_#{user}" do
FWIW, eu usei o link várias vezes, o que permite que você crie / remova usuários com base em atributos e databags.
Alguém pode me explicar como funciona o chef? Essa é uma questão bastante ampla, então, para reduzi-la, tenho essa receita muito simples que faz um loop sobre uma lista de usuários e cria cada um deles, se eles ainda não existirem. Não funciona.
Pelo que eu posso dizer, o loop parece estar acontecendo como eu esperava. Uma vez que o loop tenha completado meus comandos bash para criar cada usuário são executados, uma vez para cada iteração no loop. No entanto, quando os comandos bash são executados, eles parecem ter apenas o valor do usuário da primeira iteração do loop.
Qual é a maneira correta de escrever uma receita que faça um loop sobre dados variáveis semelhantes a este exemplo?
Aqui está a receita:
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
Estou testando isso usando o Vagrant com a seguinte configuração:
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
As mensagens geradas pelas declarações puts na receita são assim:
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
torne seu nome de script exclusivo ...
bash "create_user_#{user}" do
FWIW, eu usei o link várias vezes, o que permite que você crie / remova usuários com base em atributos e databags.
O comportamento que você está vendo pode ser explicado pela compreensão da diferença entre dois dos principais estágios de uma execução do cliente Chef: compilação e convergência.
Durante a fase de "compilação", o cliente Chef executa o código em suas receitas para criar uma coleção de recursos . Esta é uma lista dos Recursos que você disse ao Chef para gerenciar em seu sistema, junto com o estado de destino deles. Por exemplo, um recurso do Diretório para dizer que /tmp/foo
deve existir e ser de propriedade de root:
directory "/tmp/foo" do
owner "root"
end
Durante a fase "convergir", o cliente Chef usa provedores para carregar o estado atual de cada recurso e compara isso com o estado de destino. Se forem diferentes, o Chef atualizará o sistema. Para nosso recurso de diretório, o Chef criaria o diretório, se ele não existisse, e alteraria seu proprietário para "root", se necessário.
Os recursos são identificados exclusivamente por seu nome e tipo - nosso Diretório seria directory[/tmp/foo]
. Coisas estranhas acontecerão quando você tiver dois recursos com o mesmo nome, mas atributos diferentes - isso explica seu problema e pode ser corrigido usando a resposta de 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
No entanto, neste caso em particular, você se beneficiaria do uso do recurso Usuário do Chef. Aqui está um substituto para sua receita (sem as mensagens de depuração):
node[:users].each do |u|
user u['username'] do
action :create
end
end
Por que isso é melhor que um conjunto de recursos básicos?
Mas a melhor razão para usar os recursos certos é que ele comunica mais claramente sua intenção. Seu objetivo provavelmente não é executar um monte de comandos shell - é para garantir que alguns usuários estejam presentes em seu sistema. Os comandos usados para fazer isso são apenas um detalhe de implementação.
Os provedores encapsulam esses detalhes, deixando-nos livres para nos concentrar em descrever o que queremos.