Spec-Zone.ru › Chef 16

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

[править на GitHub]

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

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

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

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

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

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

Начиная с Chef Infra Client 15.x, в Chef Infra Client поставляется Ruby 2.6.

Основы 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

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 должно быть вашим именем (например, «Иванов Иван»)
  • 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

Символы или строки?

Предпочитайте строки символам, поскольку они легче читаются, и вам не нужно объяснять не-программистам на Ruby, что такое символ. Пожалуйста, модернизируйте старые кулинарные книги по мере их обнаружения.

Верно:

default['foo']['bar'] = 'baz'

Неверно:

default[:foo][:bar] = 'baz'

Кавычки для строк

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

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

Когда синтаксис %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