Учитывая следующий рецепт:
ruby_block "block1" do
block do
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source "https://yahoo.com"
end
Я ожидал, что сначала запустится ruby_block (потому что он идет первым), а затем remote_file.
Я хотел бы использовать ruby_block, чтобы определить URL-адрес удаленного_файла для загрузки, поэтому порядок важен.
Если бы не мои операторы put (), я бы предположил, что они запускаются в ожидаемом порядке, потому что в журнале написано:
==> default: [2014-06-12T17:49:19+00:00] INFO: ruby_block[block1] called
==> default: [2014-06-12T17:49:19+00:00] INFO: remote_file[/tmp/foo] created file /tmp/foo
==> default: [2014-06-12T17:49:20+00:00] INFO: remote_file[/tmp/foo] updated file contents /tmp/foo
Но выше мои операторы put () выглядят следующим образом:
==> default: in remote_file
==> default: in block1
Если вы думаете, что ресурсы запускаются в ожидаемом порядке, рассмотрите этот рецепт:
ruby_block "block1" do
block do
node.default['test'] = {}
node.default['test']['foo'] ='https://google.com'
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source node.default['test']['foo']
end
Это не удается следующим образом:
==> default: [2014-06-12T17:55:38+00:00] ERROR: {} is not a valid `source` parameter for remote_file. `source` must be an absolute URI or an array of URIs.
==> default: [2014-06-12T17:55:38+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
Строка «in block1» не отображается в выводе, поэтому ruby_block никогда не запускался.
Итак, вопрос в том, как я могу заставить ruby_block запускаться и запускаться первым?
Хороший вопрос - оба ваших примера работают так, как я ожидал, но не сразу понятно почему.
Как написал Стивен Кинг в своем ответе, первое, что нужно понять, это то, что рецепты компилируются (для создания набора ресурсов), а затем ресурсы объединяются (для внесения изменений в вашу систему). Эти две фазы часто чередуются - некоторые из ваших ресурсов могут быть объединены до того, как Chef завершит компиляцию всех ваших рецептов. Эрик Холленсбе подробно рассказывает об этом в своем посте. "Очередь выполнения ресурсов Chef".
Вот еще раз ваш первый пример:
ruby_block "block1" do
block do
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source "https://yahoo.com"
end
Это шаги, которые Chef выполнит при обработке этого примера.
ruby_block[block1]
добавляется в коллекцию ресурсов. Содержимое блока (первый puts
оператор) еще не запускается - он сохраняется для запуска при конвергенции этого ресурса.remote_file[/tmp/foo/]
добавляется в коллекцию ресурсов с источником "https://yahoo.com". В процессе составления этой декларации второй puts
Оператор будет выполнен - это имеет побочный эффект печати «в удаленном_файле», но не влияет на ресурс, который помещается в коллекцию ресурсов.ruby_block[block1]
, а Chef запускает код ruby в блоке - печатая «в блоке1». После завершения выполнения блока он регистрирует сообщение о том, что ресурс был вызван.remote_file[/tmp/foo]
. Опять же, он регистрирует сообщение (или два), связанное с этим действием.Это должно привести к следующей последовательности вывода:
К вашему второму примеру:
ruby_block "block1" do
block do
node.default['test'] = {}
node.default['test']['foo'] ='https://google.com'
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source node.default['test']['foo']
end
Как и в первом примере, мы не ожидаем, что что-либо будет напечатано, пока ruby_block компилируется - весь «блок» сохраняется, и его содержимое не запускается, пока этот ресурс не сойдется.
Первый вывод, который мы видим, находится "в удаленном_файле", поскольку puts
Оператор выполняется, когда Chef компилирует ресурс remote_file. В следующей строке мы устанавливаем source
параметр на значение node.default['test']['foo']
, что очевидно {}
. Это недопустимое значение для source
, поэтому выполнение Chef завершается на этом этапе - до того, как код в ruby_block
когда-либо работает.
Следовательно, ожидаемый результат этого рецепта:
source
параметрНадеюсь, это поможет вам понять наблюдаемое вами поведение, но у нас все еще есть проблема, которую нужно решить.
Хотя вы спросили: «Как я могу заставить ruby_block запускаться первым?», Ваш комментарий StephenKing предполагает, что это не совсем то, что вы хотите - если вы действительно хотите, чтобы этот блок запускался первым, вы можете поместить его прямо в код рецепта. В качестве альтернативы вы можете использовать .run_action () метод чтобы заставить ресурс объединиться, как только он скомпилирован, но вы говорите, что еще есть ресурсы, которые необходимо объединить, прежде чем ruby_block станет полезным.
Как мы видели выше, ресурсы не «запускаются», они сначала «компилируются», а затем «объединяются». Имея это в виду, вам нужно remote_file
ресурс для использования некоторых данных, которые неизвестны на момент компиляции, но будут известны при схождении. Другими словами, что-то вроде параметра «блок» в ruby_block
- фрагмент кода, который запускается позже. Что-то вроде этого:
remote_file "/tmp/foo" do
puts "in remote_file"
# this syntax isn't valid...
source do
node.default['test']['foo']
end
end
К счастью, такая штука существует - она называется Ленивая оценка атрибутов. Используя эту функцию, ваш второй пример будет выглядеть так:
ruby_block "block1" do
block do
node.default['test'] = {}
node.default['test']['foo'] = 'https://google.com'
puts "in block1"
end
action :create
end
remote_file "/tmp/foo" do
puts "in remote_file"
source lazy { node['test']['foo'] }
end
И ожидаемый результат этого рецепта?
У шеф-повара есть этап компиляции и запуска. Я предполагаю, что код внутри ruby_block
не выполняется на этапе компиляции, потому что он находится внутри block
оператор (который затем будет выполнен на этапе выполнения). В puts
внутри remote_file
однако блок находится на «уровне атрибута» внутри определения ресурса, которое на самом деле выполняется шеф-поваром (на самом деле, afaik source node.default...
это вызов функции).
Итак, если я правильно понял, то, что вы хотите сделать, можно сделать с помощью следующего кода:
node.default['test']['foo'] ='https://google.com'
remote_file "/tmp/foo" do
source node['test']['foo']
end
Если установить атрибуты через node.default
не работает, используйте node.set
.
Также отметьте, что я не читаю атрибут через node.default[]
, но напрямую через node[]
. В противном случае приоритет атрибута особенность шеф-повара не имеет смысла.