Как лениво оценить произвольную переменную с шеф-поваром

Я пишу рецепт шеф-повара, чтобы установить наш код приложения и выполнить его. Рецепт должен быть особенно посвящен каталогу, в котором находится этот код (для запуска шаблонов, настройки пересылки журналов и т.д.). Таким образом, сам каталог появляется во многих местах в разных рецептах.

Я пытаюсь получить/определить переменную, чтобы я мог повторно использовать ее в своем блоке ресурсов со строчной интерполяцией. Это довольно просто:

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

У меня есть несколько обходных решений, но я хотел бы, чтобы конкретное решение, чтобы домашняя переменная принималась только во время выполнения рецепта, а не время компиляции.


Обходной путь 1

Динамически оценивать домашнюю переменную внутри блока ruby, например:

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

На самом деле это не работает, предоставляя метод NoMethodError undefined для 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

Но было бы здорово просто сохранить эту строку кода один раз, сохранить ее в переменной и сделать с ней.

Обходной путь 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 (что является нечетной синтаксической ошибкой, но что угодно). Есть ли законный способ сделать это?

Ответ 1

Я не тестировал этот конкретный код, но я сделал что-то подобное в кулинарных книгах и использовал лямбда для оценки задержки следующим образом:

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

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

Ответ 2

Для ruby_block любые переменные внутри блока должны быть глобальными, поскольку все, что определено внутри блока, является локальным для него.

Вы не можете использовать lambda для отложенного выполнения в библиотеке, поэтому ruby_block хорошо работает в этом случае.

Ответ 3

@thoughtcroft ответ не работает для меня на шеф-поваре 12.8.1

В этом случае я помещаю необходимый код в пользовательский ресурс и вызываю его с ленивыми атрибутами.

mycookbook_myresource "name" do
  attribute lazy { myvar }
end

Не изящное решение, но оно работает для меня.