web-dev-qa-db-ja.com

Chefで任意の変数を怠惰に評価する方法

アプリケーションコードをインストールして実行するためのChefレシピを書いています。レシピは、このコードが最終的に配置されるディレクトリに注意する必要があります(テンプレートの実行、ログ転送の設定など)。したがって、ディレクトリ自体は、さまざまなレシピの多くの場所に表示されます。

文字列補間を使用してリソースブロックで変数を再利用できるように、変数をフェッチ/定義しようとしています。これは非常に簡単です。

home = node['etc']['passwd'][node['nodejs']['user']]['dir']

使用例として、次のように、リポジトリのダウンロードをホームディレクトリに配置するように指示しながらnpm installを実行します。

execute "npm install" do
  command "npm install #{prefix}#{app} --prefix #{home}"
end

home変数を定義する最初のブロックがコンパイル時に実行されることを除いて。 nodejsユーザーアカウントがまだ存在していない可能性のある新しいサーバーでは、これは問題であり、

NoMethodError undefined method '[]' for nil:NilClass

いくつかの回避策がありますが、home変数がコンパイル時ではなく、レシピの実行時にのみフェッチされるようにする特定のソリューションが必要です。


回避策1

次のように、Rubyブロック内のホーム変数を動的に評価します:

Ruby_block "fetch home dir" do
  block do
    home = node['etc']['passwd'][node['nodejs']['user']]['dir']
  end
end

これは実際には機能していないようで、次のようなことを行おうとすると、 NoMethodError undefined method home for Chef :: Resource :: Directory が与えられます。

directory ".npm" do
  path "#{home}/.npm"
end

私はここで何か間違ったことをしているに違いないと感じています。

回避策2

パラメータを必要とするすべてのリソースについて、パラメータを怠惰に評価します。したがって、代わりにこれを行います。

directory ".npm" do
  path lazy "#{node['etc']['passwd'][node['nodejs']['user']]['dir']}/.npm"
end

しかし、そのコード行を1回維持し、それを変数に格納して、それを実行するだけでよいのは本当に素晴らしいことです。

回避策3

コンパイル時にユーザーを作成します。これはもちろん、次のように ここにリンクされているトリックを通知する を使用して機能します。

u = user node['nodejs']['user'] do
  comment "The #{node['nodejs']['user']} is the user we want all our nodejs apps will run under."
  username node['nodejs']['user']
  home "/home/#{node['nodejs']['user']}"
end

u.run_action(:create)

これで問題は正確に解決しますが、変数の評価を遅らせる機能が必要になる場合もあるので、質問をそのままにしておきます。

私が欲しいもの

本当にやりたいです

home lazy = node['etc']['passwd'][node['nodejs']['user']]['dir']

しかし、それは合法的な構文ではなく、 NameErrorを与えると、ubuntuバージョン13.10 でホームのリソースが見つかりません(これは奇妙な構文エラーですが、何でも)。これを達成するための合法的な方法はありますか?

14
Patrick M

この特定のコードはテストしていませんが、クックブックで同様のことを行い、ラムダを使用して次のように評価を遅らせました。

home = lambda {node['etc']['passwd'][node['nodejs']['user']]['dir']}

execute "npm install" do
  command "npm install #{prefix}#{app} --prefix #{home.call}"
end
18
thoughtcroft

にとって Ruby_block、ブロック内で定義されたものはすべてローカルであるため、ブロック内の変数はグローバルである必要があります。

ライブラリでの遅延実行にラムダを使用することはできないため、Ruby_blockこの場合はうまく機能します。

1
Oscar Barrett

@thoughtcroftの回答がchef-client12.8.1で機能しません

この場合、必要なコードをカスタムリソースに配置し、レイジー属性を使用して呼び出します。

mycookbook_myresource "name" do
  attribute lazy { myvar }
end

エレガントな解決策ではありませんが、それは私にとってはうまくいきます。

1
e.aktec