У меня проблемы с дизайном кода марионетки (версия 5.5). Я написал компонентный модуль для работы с ucarp. Он использует модуль eyp-systemd для регистрации службы ucarp в systemd. Теперь я хотел бы использовать модуль ucarp из двух независимых модулей профиля, которые управляют разными службами (в моем случае на самом деле haproxy и bind9). По сути это выглядит так:
class ucarp {
systemd::service { 'ucarp':
# list of parameters
}
}
define ucarp::vip {
# defines a virtual IP to be shared among several nodes
}
# ====================
class profile_haproxy {
include ::ucarp
::ucarp::vip { 'haproxy': }
# setup haproxy
}
# =====================
class profile_bind9 {
include ::ucarp
::ucarp::vip { 'bind9': }
# setup bind9
}
Это просто и хорошо работает.
Теперь актуальная проблема: лучше всего заказывать услугу ucarp после услуг, которые запускаются через ucarp. Это возможно с помощью параметра after:
class ucarp(
Array[String] $after,
) {
systemd::service { 'ucarp':
after => $after.join(' '),
# list of other parameters
}
}
Это требует замены include ::ucarp
по
class { '::ucarp':
after => ['haproxy'],
}
или
class { '::ucarp':
after => ['bind9'],
}
соответственно. Конечно, это сразу же привело бы к ошибке «повторяющееся объявление».
На самом деле я хотел бы иметь единственный экземпляр класса ucarp, который собирает все after
параметры в одну строку, которую можно передать в systemd :: service. Как бы я это сделал?
На данный момент мне приходят в голову два возможных решения:
systemd::service::after
который управляет соответствующей записью в файле определения службы. Я действительно не хочу этого делать. Обычно я уклоняюсь от модификации кузнечных модулей, так как это заставляет меня поддерживать их самостоятельно. В этом случае изменение тоже кажется довольно большим (включая изменение интерфейса).ucarp::order_after
который ничего не делает. Модули профиля будут определять виртуальные экземпляры этого типа. Затем класс ucarp может использовать запрос puppetdb для сбора всех экземпляров ucarp::order_after
. Большой недостаток здесь в том, что я имею дело только с виртуальными ресурсами, а не с экспортируемыми. Так что на самом деле нет необходимости задействовать puppetdb, что превращает этот подход в некрасивый обходной путь.Следующее решение вдохновлено c4f4t0r:
after
Сервисы. Список after
услуги предоставляет hiera:class profile_ucarp ( Array[String] $after, ) { class { '::ucarp': after => $after, } }
profile_ucarp.after: - 'haproxy' - 'bind9'Другим классам профиля больше не нужно создавать экземпляр
ucarp
class - устранение потенциальных проблем с дублированием объявлений. Я считаю это решение лучше двух вышеупомянутых. Тем не менее, я не удовлетворен тем, что использование hiera для исправления проблемы, связанной исключительно с кодом, является неправильным использованием hiera.Я надеюсь, что есть другие возможности, о которых я не могу сейчас думать.
Вам нужно использовать функцию contain и поместить параметры класса в hiera.
В модуле вашего профиля ваши классы profile :: haproxy и profile :: bind могут быть только одним, потому что ваши классы дублируются, потому что вы не используете hiera для хранения параметров класса
class profile::ucarp {
contain ::ucarp
contain ::ucarp::vip
}
class profile::haproxy {
contain ::haproxy
}
#now I can create a role using this profiles
class role::elb {
contain ::profile::ucarp
contain ::profile::haproxy
}
Теперь внутри hiera вы можете хранить параметры в зависимости от функциональности хоста, если вы хотите избежать ошибки, попробуйте проверить свой дизайн с помощью ролей и профилей марионеточного документа.
Из марионеточной документации
Having classes contain other classes can be very useful, especially in larger modules where you want to improve code readability by moving chunks of implementation into separate files.
However, unlike resources, Puppet does not automatically contain classes when they are declared inside another class. This is because classes can be declared in several places via include and similar functions. Most of these places shouldn’t contain the class, and trying to contain it everywhere would cause huge problems.
Instead, you must manually contain any classes that need to be contained.