Spec-Zone.ru › Chef 17

Руководство по Ruby

[редактировать на GitHub]

Ruby — простой язык программирования:

  • Chef использует Ruby в качестве языка для определения шаблонов, встречающихся в ресурсах, рецептах и кулинарных книгах
  • Используйте эти шаблоны для настройки, развертывания и управления узлами по всей сети

Ruby также является мощным и полным языком программирования:

  • Используйте язык программирования Ruby для принятия решений о том, что должно произойти с конкретными ресурсами и рецептами
  • Расширяйте Chef любым способом, необходимым вашей организации

Чтобы узнать больше о Ruby, см.:

  • Документация Ruby
  • Документация стандартной библиотеки Ruby

В Chef Infra Client 15 используется Ruby 2.6, а в Chef Infra Client 16 — Ruby 2.7.

Основы Ruby

Этот раздел охватывает основы Ruby.

Проверка синтаксиса

Многие люди, которые только начинают изучать Ruby, часто замечают, что освоение основ не занимает много времени. Например, полезно знать, как проверить синтаксис файла Ruby, такого как содержимое кулинарной книги с именем my_cookbook.rb:

ruby -c my_cookbook_file.rb

чтобы получить:

Syntax OK

Комментарии

Используйте комментарий для объяснения кода, присутствующего в кулинарной книге или рецепте. Всё, что следует за # — это комментарий.

# This is a comment.

Локальные переменные

Присвойте локальную переменную:

x = 1

Математические операции

Выполните некоторые базовые арифметические операции:

1 + 2           # => 3
2 * 7           # => 14
5 / 2           # => 2   (because both arguments are whole numbers)
5 / 2.0         # => 2.5 (because one of the numbers had a decimal place)
1 + (2 * 3)     # => 7   (you can use parentheses to group expressions)

Строки

Работа со строками:

'single quoted'   # => "single quoted"
"double quoted"   # => "double quoted"
'It\'s alive!'    # => "It's alive!" (the \ is an escape character)
'1 + 2 = 5'       # => "1 + 2 = 5" (numbers surrounded by quotes behave like strings)

Преобразуйте строку в верхний или нижний регистр. Например, имя хоста «Foo»:

node['hostname'].downcase    # => "foo"
node['hostname'].upcase      # => "FOO"

Ruby в строках

Встраивание Ruby в строку:

x = 'Bob'
"Hi, #{x}"      # => "Hi, Bob"
'Hello, #{x}'   # => "Hello, \#{x}" Notice that single quotes don't work with #{}

Символ экранирования

Используйте обратную косую черту (\) в качестве символа экранирования, когда кавычки должны появляться внутри строк. Однако, вам не нужно экранировать одинарные кавычки внутри двойных кавычек. Например:

'It\'s alive!'                        # => "It's alive!"
"Won\'t you read Grant\'s book?"      # => "Won't you read Grant's book?"

Интерполяция

Когда в строках есть кавычки внутри кавычек, используйте двойные кавычки (" ") для внешних кавычек, а затем одинарные кавычки (' ') для внутренних кавычек. Например:

Chef::Log.info("Loaded from aws[#{aws['id']}]")
"node['mysql']['secretpath']"
"#{ENV['HOME']}/chef.txt"
antarctica_hint = hint?('antarctica')
if antarctica_hint['snow']
  "There are #{antarctica_hint['penguins']} penguins here."
else
  'There is no snow here, and penguins like snow.'
end

Истинные значения

Работа с базовыми истинными значениями:

true            # => true
false           # => false
nil             # => nil
0               # => true ( the only false values in Ruby are false
                #    and nil; in other words: if it exists in Ruby,
                #    even if it exists as zero, then it is true.)
1 == 1          # => true ( == tests for equality )
1 == true       # => false ( == tests for equality )

Ложные значения

Работа с базовыми ложными значениями (! означает «не»!):

!true           # => false
!false          # => true
!nil            # => true
1 != 2          # => true (1 is not equal to 2)
1 != 1          # => false (1 is not equal to itself)

Преобразование истинных значений

Преобразование чего-либо в true или false (!! означает «не не»!):

!!true          # => true
!!false         # => false
!!nil           # => false (when pressed, nil is false)
!!0             # => true (zero is NOT false).

Массивы

Создание списков с помощью массивов:

x = ['a', 'b', 'c']   # => ["a", "b", "c"]
x[0]                  # => "a" (zero is the first index)
x.first               # => "a" (see?)
x.last                # => "c"
x + ['d']             # => ["a", "b", "c", "d"]
x                     # => ["a", "b", "c"] ( x is unchanged)
x = x + ['d']         # => ["a", "b", "c", "d"]
x                     # => ["a", "b", "c", "d"]

Массивы со пробелами

Синтаксис %w — это сокращение Ruby для создания массива без необходимости в кавычках и запятых вокруг элементов.

Например:

if %w(debian ubuntu).include?(node['platform'])
  # do debian/ubuntu things with the Ruby array %w() shortcut
end

Когда синтаксис %w использует переменную, например |foo|, следует использовать строки в двойных кавычках.

Верно:

%w(openssl.cnf pkitool vars Rakefile).each do |foo|
  template "/etc/openvpn/easy-rsa/#{foo}" do
    source "#{foo}.erb"
    ...
  end
end

Неверно:

%w(openssl.cnf pkitool vars Rakefile).each do |foo|
  template '/etc/openvpn/easy-rsa/#{foo}' do
    source '#{foo}.erb'
    ...
  end
end
Пример

WiX включает несколько инструментов — такие как candle (предварительно обрабатывает и компилирует исходные файлы в объектные файлы), light (связывает и объединяет объектные файлы с базой данных установщика) и heat (извлекает файлы из различных входных форматов). Следующий пример использует массив со пробелами и ресурс проверки аудита Chef InSpec file для проверки наличия этих трех инструментов:

%w(
  candle.exe
  heat.exe
  light.exe
).each do |utility|
  describe file("C:/wix/#{utility}") do
    it { should be_file }
  end
end

Хэш

Хэш — это список с ключами и значениями. Иногда у хэшей нет определенного порядка:

h = {
  'first_name' => 'Bob',
  'last_name'  => 'Jones',
}

А иногда он есть. Например, сначала имя, потом фамилия:

h.keys              # => ["first_name", "last_name"]
h['first_name']     # => "Bob"
h['last_name']      # => "Jones"
h['age'] = 23
h.keys              # => ["first_name", "age", "last_name"]
h.values            # => ["Jones", "Bob", 23]

Регулярные выражения

Используйте регулярные выражения в стиле Perl:

'I believe'  =~ /I/                       # => 0 (matches at the first character)
'I believe'  =~ /lie/                     # => 4 (matches at the 5th character)
'I am human' =~ /bacon/                   # => nil (no match - bacon comes from pigs)
'I am human' !~ /bacon/                   # => true (correct, no bacon here)
/give me a ([0-9]+)/ =~ 'give me a 7'     # => 0 (matched)

Утверждения

Используйте условия! Например, оператор if

if false
  # this won't happen
elsif nil
  # this won't either
else
  # code here will run though
end

или оператор case:

x = 'dog'
case x
when 'fish'
 # this won't happen
when 'dog', 'cat', 'monkey'
  # this will run
else
  # the else is an optional catch-all
end

if

Оператор if может использоваться для указания части рецепта, которая должна использоваться, когда выполняются определенные условия. Операторы else и elsif могут использоваться для обработки ситуаций, когда начальное условие не выполняется или когда существуют другие возможные условия, которые могут быть выполнены. Поскольку это поведение на 100% Ruby, делайте это в рецепте так же, как и в любом другом месте.

Например, использование оператора if с атрибутом узла platform:

if node['platform'] == 'ubuntu'
  # do ubuntu things
end
модификатор if

if может использоваться в качестве модификатора, который выполняет левую часть выражения, если правая часть выражения истинна. Выражение модификатора if должно быть однострочным, и операторы else и elsif не поддерживаются.

В приведенном ниже примере функция do_ubuntu_thing будет выполняться, если платформа на узле — Ubuntu.

do_ubuntu_thing if platform?('ubuntu')

case

Оператор case может использоваться для обработки ситуации, когда есть много условий. Используйте оператор when для каждого условия, сколько потребуется.

Например, использование оператора case с атрибутом узла platform:

case node['platform']
when 'debian', 'ubuntu'
  # do debian/ubuntu things
when 'redhat', 'centos', 'fedora'
  # do redhat/centos/fedora things
end

Например, использование оператора case с атрибутом узла platform_family:

case node['platform_family']
when 'debian'
  # do things on debian-ish platforms (debian, ubuntu, linuxmint)
when 'rhel'
  # do things on RHEL platforms (redhat, centos, scientific, etc)
end

Вызов метода

Вызов метода на чем-либо с помощью .method_name():

x = 'My String'
x.split(' ')            # => ["My", "String"]
x.split(' ').join(', ') # => "My, String"

Определение метода

Определение метода (или функции, если хотите):

def do_something_useless(first_argument, second_argument)
  puts "You gave me #{first_argument} and #{second_argument}"
end

do_something_useless('apple', 'banana')
# => "You gave me apple and banana"
do_something_useless 1, 2
# => "You gave me 1 and 2"
# see how the parentheses are optional if there's no confusion about what to do

Класс Ruby

Использование класса Ruby File в рецепте. Поскольку у Chef есть ресурс file, используйте File для использования класса Ruby File. Например:

execute 'apt-get-update' do
  command 'apt-get update'
  ignore_failure true
  not_if { ::File.exist?('/var/lib/apt/periodic/update-success-stamp') }
end

Включение класса

Использование :include для включения другого класса Ruby. Например:

::Chef::DSL::Recipe.include MyCookbook::Helpers

В не-Chef Ruby синтаксис include (без префикса :). Однако без префикса : Chef Infra Client будет искать провайдер с именем include. Использование префикса : указывает Chef Infra Client на поиск указанного класса, который следует за ним.

Включение параметра

Метод include? может использоваться для обеспечения включения определенного параметра перед выполнением действия. Например, использование метода include? для поиска определенного параметра:

if %w(debian ubuntu).include?(node['platform'])
  # do debian/ubuntu things
end

или:

if %w(rhel).include?(node['platform_family'])
  # do RHEL things
end

Рекомендации по шаблонам

Этот раздел описывает лучшие практики для написания кулинарных книг и рецептов.

Этикет Git

Хотя это не строго вопрос стиля Chef, убедитесь, что user.name и user.email правильно заданы в вашем файле .gitconfig.

  • user.name должно быть вашим именем (например, «Julian Dunn»)
  • user.email должен быть действующим электронным адресом

Это предотвратит появление записей в журнале коммитов, похожих на "guestuser <login@Bobs-Macbook-Pro.local>", которые бесполезны.

Использование дефисов

Имена кулинарных книг и пользовательских ресурсов должны содержать только буквенно-цифровые символы. Дефис (-) является допустимым символом и может использоваться в именах кулинарных книг и пользовательских ресурсов, но он не рекомендуется. Chef Infra Client вернет ошибку, если дефис не будет преобразован в подчеркивание (_) при ссылке в рецепте на имя пользовательского ресурса, в котором есть дефис.

Наименование кулинарных книг

Используйте короткий организационный префикс для кулинарных книг приложений, которые являются частью вашей организации. Например, если ваша организация называется SecondMarket, используйте sm в качестве префикса: sm_postgresql или sm_httpd.

Версионирование кулинарных книг

  • Используйте семантическое версионирование при нумерации кулинарных книг.
  • Загружайте только стабильные кулинарные книги из ветки master.
  • Загружайте только нестабильные кулинарные книги из ветки dev. Объедините их в master и увеличьте версию при переходе в стабильное состояние.
  • Всегда обновляйте CHANGELOG.md любыми изменениями, указав JIRA-номер и краткое описание.

Наименование

Именуйте вещи единообразно для системы и компонента. Например:

  • атрибуты: node['foo']['bar']
  • рецепт: foo::bar
  • роль: foo-bar
  • каталоги: foo/bar (если специфично для компонента), foo (если нет). Например: /var/log/foo/bar.

Именуйте атрибуты по рецепту, в котором они в основном используются. Например, node['postgresql']['server'].

Порядок параметров

Придерживайтесь следующего порядка информации в каждом заявлении о ресурсе:

  • Источник
  • Кулинарная книга
  • Владение ресурсом
  • Разрешения
  • Уведомления
  • Действие

Например:

template '/tmp/foobar.txt' do
  source 'foobar.txt.erb'
  owner  'someuser'
  group  'somegroup'
  mode   '0644'
  variables(
    foo: 'bar'
  )
  notifies :reload, 'service[whatever]'
  action :create
end

Режимы файлов

Всегда указывайте режим файла с помощью цитируемой строки из 3-5 символов, определяющей восьмеричный режим:

mode '755'
mode '0755'

Неверно:

mode 755

Указать действие ресурса?

Объявление ресурса не требует указания действия, так как Chef Infra Client автоматически применит значение по умолчанию для ресурса, если оно не указано в блоке ресурса. Например:

package 'monit'

будет устанавливать пакет monit, поскольку действие :install является действием по умолчанию для ресурса package.

Однако, если желательно обеспечить удобочитаемость кода, например, убедиться, что читатель понимает, какое действие по умолчанию используется для пользовательского ресурса или указать действие для ресурса, значение по умолчанию которого может быть не очевидным для читателя, рекомендуется указать действие по умолчанию:

ohai 'apache_modules' do
  action :reload
end

Оформление строк

Используйте строковые литералы в одинарных кавычках во всех случаях, когда в строке не требуется интерполяция.

Массивы пробелов

Когда синтаксис %w использует переменную, например |foo|, следует использовать строковые литералы в двойных кавычках.

Правильно:

%w(openssl.cnf pkitool vars Rakefile).each do |foo|
  template "/etc/openvpn/easy-rsa/#{foo}" do
    source "#{foo}.erb"
    ...
  end
end

Неправильно:

%w(openssl.cnf pkitool vars Rakefile).each do |foo|
  template '/etc/openvpn/easy-rsa/#{foo}' do
    source '#{foo}.erb'
    ...
  end
end

Рецепты

Рецепт должен быть чистым и содержать подробные комментарии. Например:

###########
# variables
###########

connection_info = {
  host: '127.0.0.1',
  port: '3306',
  username: 'root',
  password: 'm3y3sqlr00t',
}

#################
# Mysql resources
#################

mysql_service 'default' do
  port '3306'
  initial_root_password 'm3y3sqlr00t'
  action [:create, :start]
end

mysql_database 'wordpress_demo' do
  connection connection_info
  action :create
end

mysql_database_user 'wordpress_user' do
  connection connection_info
  database_name 'wordpress_demo'
  password 'w0rdpr3ssdem0'
  privileges [:create, :delete, :select, :update, :insert]
  action :grant
end

##################
# Apache resources
##################

httpd_service 'default' do
  listen_ports %w(80)
  mpm 'prefork'
  action [:create, :start]
end

httpd_module 'php' do
  notifies :restart, 'httpd_service[default]'
  action :create
end

###############
# Php resources
###############

package 'php-gd' do
  action :install
end

package 'php-mysql' do
  action :install
end

directory '/etc/php.d' do
  action :create
end

template '/etc/php.d/mysql.ini' do
  source 'mysql.ini.erb'
  action :create
end

httpd_config 'php' do
  source 'php.conf.erb'
  notifies :restart, 'httpd_service[default]'
  action :create
end

#####################
# wordpress resources
#####################

directory '/srv/wordpress_demo' do
  user 'apache'
  recursive true
  action :create
end

tar_extract 'https://wordpress.org/wordpress-4.1.tar.gz' do
  target_dir '/srv/wordpress_demo'
  tar_flags ['--strip-components 1']
  user 'apache'
  creates '/srv/wordpress_demo/index.php'
  action :extract
end

directory '/srv/wordpress_demo/wp-content' do
  user 'apache'
  action :create
end

httpd_config 'wordpress' do
  source 'wordpress.conf.erb'
  variables(
    servername: 'wordpress',
    server_aliases: %w(computers.biz www.computers.biz),
    document_root: '/srv/wordpress_demo'
  )
  notifies :restart, 'httpd_service[default]'
  action :create
end

template '/srv/wordpress_demo/wp-config.php' do
  source 'wp-config.php.erb'
  owner 'apache'
  variables(
    db_name: 'wordpress_demo',
    db_user: 'wordpress_user',
    db_password: 'w0rdpr3ssdem0',
    db_host: '127.0.0.1',
    db_prefix: 'wp_',
    db_charset: 'utf8',
    auth_key: 'You should probably use randomly',
    secure_auth_key: 'generated strings. These can be hard',
    logged_in_key: 'coded, pulled from encrypted databags,',
    nonce_key: 'or a ruby function that accessed an',
    auth_salt: 'arbitrary data source, such as a password',
    secure_auth_salt: 'vault. Node attributes could work',
    logged_in_salt: 'as well, but you take special care',
    nonce_salt: 'so they are not saved to your chef-server.',
    allow_multisite: 'false'
  )
  action :create
end

Проверка кода Cookstyle

Chef Workstation включает Cookstyle для проверки соблюдения стиля Ruby и Chef в вашем кулинарном коде. Все кулинарные книги должны соответствовать правилам Cookstyle перед загрузкой.

cookstyle your-cookbook

должно возвращать no offenses detected

© Chef Software, Inc.
Licensed under the Creative Commons Attribution 3.0 Unported License.
The Chef™ Mark and Chef Logo are either registered trademarks/service marks or trademarks/servicemarks of Chef, in the United States and other countries and are used with Chef Inc's permission.
We are not affiliated with, endorsed or sponsored by Chef Inc.
https://docs.chef.io/ruby/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API