Руководство по Ruby
Ruby — простой язык программирования:
- Chef использует Ruby в качестве языка для определения шаблонов, встречающихся в ресурсах, рецептах и кулинарных книгах
- Используйте эти шаблоны для настройки, развертывания и управления узлами по всей сети
Ruby также является мощным и полным языком программирования:
- Используйте язык программирования Ruby для принятия решений о том, что должно произойти с конкретными ресурсами и рецептами
- Расширяйте Chef любым способом, необходимым вашей организации
Чтобы узнать больше о 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/