誰かが私にシェフの仕組みを説明してくれませんか?それはかなり広い質問なので、絞り込むために、ユーザーのリストをループして、まだ存在しない場合はそれぞれを作成するこの非常に単純なレシピを持っています。それは動作しません。
私が言うことができることから、ループは期待どおりに起こっているようです。ループが完了すると、各ユーザーを作成するためのbashコマンドが実行されます(ループの各反復ごとに1回)。ただし、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
スクリプト名を一意にします...
bash "create_user_#{user}" do
FWIW、私は https://github.com/fnichol/chef-user を数回使用しました。これにより、属性とデータバッグに基づいてユーザーを作成/削除できます。
あなたが見ている振る舞いは、Chefクライアントの実行における2つの重要な段階、コンパイルと収束の違いを理解することで説明できます。
「コンパイル」フェーズでは、Chefクライアントがレシピのコードを実行して、リソースコレクションを構築します。これは、システムで管理するようChefに指示した Resources のリストとそのターゲット状態のリストです。たとえば、/tmp/foo
が存在し、ルートによって所有されている必要があります:
directory "/tmp/foo" do
owner "root"
end
「収束」フェーズでは、Chefクライアントはprovidersを使用して各リソースの現在の状態をロードし、これをターゲットの状態と比較します。それらが異なる場合、Chefはシステムを更新します。ディレクトリリソースの場合、Chefは存在しない場合はディレクトリを作成し、必要に応じて所有者を「root」に変更します。
リソースは名前とタイプによって一意に識別されます-私たちのディレクトリはdirectory[/tmp/foo]
。名前が同じで属性が異なる2つのリソースがある場合、奇妙なことが起こります。これは問題を説明するものであり、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リソースのセットよりも優れているのですか?
しかし、適切なリソースを使用する最も良い理由は、意図をより明確に伝えることです。あなたの目標はおそらく、一連のシェルコマンドを実行することではありません-システムに何人かのユーザーが存在することを確認することです。これを行うために使用されるコマンドは、単なる実装の詳細です。
プロバイダーはそれらの詳細をカプセル化し、私たちが何をしたいかを説明することに集中できるようにします。