Назад | Перейти на главную страницу

Поварский рецепт порядок выполнения redux

Учитывая следующий рецепт:

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 выполнит при обработке этого примера.

  1. Сначала компилируется объявление ruby_block, в результате получается ресурс с именем ruby_block[block1] добавляется в коллекцию ресурсов. Содержимое блока (первый puts оператор) еще не запускается - он сохраняется для запуска при конвергенции этого ресурса.
  2. Затем компилируется объявление remote_file. Это приводит к ресурсу под названием remote_file[/tmp/foo/] добавляется в коллекцию ресурсов с источником "https://yahoo.com". В процессе составления этой декларации второй puts Оператор будет выполнен - ​​это имеет побочный эффект печати «в удаленном_файле», но не влияет на ресурс, который помещается в коллекцию ресурсов.
  3. Не имея ничего другого для компиляции, Chef начинает объединять ресурсы в коллекции ресурсов. Первый ruby_block[block1], а Chef запускает код ruby ​​в блоке - печатая «в блоке1». После завершения выполнения блока он регистрирует сообщение о том, что ресурс был вызван.
  4. Наконец, Шеф сходится remote_file[/tmp/foo]. Опять же, он регистрирует сообщение (или два), связанное с этим действием.

Это должно привести к следующей последовательности вывода:

  1. При компиляции ruby_block ничего не печатается.
  2. "в удаленном_файле" будет напечатано, пока удаленный_файл компилируется.
  3. "in block1" будет напечатано, пока ruby_block объединяется.
  4. Сообщение журнала Chef будет напечатано после объединения ruby_block.
  5. Другие сообщения журналов Chef будут напечатаны во время / после объединения remote_file.

К вашему второму примеру:

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 когда-либо работает.

Следовательно, ожидаемый результат этого рецепта:

  1. Нет вывода при компиляции ruby_block
  2. "в удаленном_файле" печатается при компиляции удаленного_файла
  3. Ошибка из-за недопустимого 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

И ожидаемый результат этого рецепта?

  1. Нет вывода при компиляции ruby_block
  2. "в удаленном_файле" печатается при компиляции удаленного_файла
  3. "in block1" печатается при схождении ruby_block
  4. Сообщение журнала Chef, показывающее, что ruby_block был объединен
  5. Сообщения журнала Chef, показывающие, что удаленный_файл был объединен

У шеф-повара есть этап компиляции и запуска. Я предполагаю, что код внутри 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[]. В противном случае приоритет атрибута особенность шеф-повара не имеет смысла.